mirror of
https://github.com/crystalidea/qt6windows7.git
synced 2025-07-03 07:45:30 +08:00
qt 6.5.1 original
This commit is contained in:
43
tests/auto/gui/text/CMakeLists.txt
Normal file
43
tests/auto/gui/text/CMakeLists.txt
Normal file
@ -0,0 +1,43 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
add_subdirectory(qabstracttextdocumentlayout)
|
||||
add_subdirectory(qfont)
|
||||
add_subdirectory(qfontdatabase)
|
||||
add_subdirectory(qfontmetrics)
|
||||
add_subdirectory(qglyphrun)
|
||||
add_subdirectory(qrawfont)
|
||||
add_subdirectory(qstatictext)
|
||||
add_subdirectory(qsyntaxhighlighter)
|
||||
add_subdirectory(qtextblock)
|
||||
add_subdirectory(qtextcursor)
|
||||
add_subdirectory(qtextdocumentfragment)
|
||||
add_subdirectory(qtextdocumentlayout)
|
||||
add_subdirectory(qtextformat)
|
||||
add_subdirectory(qtextimagehandler)
|
||||
add_subdirectory(qtextlist)
|
||||
add_subdirectory(qtextobject)
|
||||
# add_subdirectory(qtextscriptengine) # disable until system_harfbuzz feature is available
|
||||
add_subdirectory(qtexttable)
|
||||
add_subdirectory(qinputcontrol)
|
||||
if(QT_FEATURE_private_tests AND TARGET Qt::Xml)
|
||||
add_subdirectory(qcssparser)
|
||||
endif()
|
||||
if(QT_FEATURE_private_tests)
|
||||
add_subdirectory(qfontcache)
|
||||
add_subdirectory(qtextlayout)
|
||||
add_subdirectory(qzip)
|
||||
add_subdirectory(qtextodfwriter)
|
||||
endif()
|
||||
if(TARGET Qt::Xml)
|
||||
add_subdirectory(qtextdocument)
|
||||
endif()
|
||||
if(QT_FEATURE_private_tests AND UNIX)
|
||||
add_subdirectory(qtextpiecetable)
|
||||
endif()
|
||||
if(QT_FEATURE_textmarkdownreader)
|
||||
add_subdirectory(qtextmarkdownimporter)
|
||||
endif()
|
||||
if(QT_FEATURE_private_tests AND QT_FEATURE_textmarkdownwriter)
|
||||
add_subdirectory(qtextmarkdownwriter)
|
||||
endif()
|
@ -0,0 +1,13 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qabstracttextdocumentlayout Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qabstracttextdocumentlayout
|
||||
SOURCES
|
||||
tst_qabstracttextdocumentlayout.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
)
|
@ -0,0 +1,209 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qdebug.h>
|
||||
#include <qabstracttextdocumentlayout.h>
|
||||
#include <qimage.h>
|
||||
#include <qtextobject.h>
|
||||
#include <qfontmetrics.h>
|
||||
|
||||
class tst_QAbstractTextDocumentLayout : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QAbstractTextDocumentLayout();
|
||||
virtual ~tst_QAbstractTextDocumentLayout();
|
||||
|
||||
private slots:
|
||||
void getSetCheck();
|
||||
void maximumBlockCount();
|
||||
void anchorAt();
|
||||
void imageAt();
|
||||
void formatAt();
|
||||
};
|
||||
|
||||
tst_QAbstractTextDocumentLayout::tst_QAbstractTextDocumentLayout()
|
||||
{
|
||||
}
|
||||
|
||||
tst_QAbstractTextDocumentLayout::~tst_QAbstractTextDocumentLayout()
|
||||
{
|
||||
}
|
||||
|
||||
class MyAbstractTextDocumentLayout : public QAbstractTextDocumentLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
MyAbstractTextDocumentLayout(QTextDocument *doc)
|
||||
: QAbstractTextDocumentLayout(doc)
|
||||
, gotFullLayout(false)
|
||||
, blockCount(0)
|
||||
, changeEvents(0)
|
||||
{
|
||||
}
|
||||
|
||||
void draw(QPainter *, const PaintContext &) override {}
|
||||
int hitTest(const QPointF &, Qt::HitTestAccuracy) const override { return 0; }
|
||||
int pageCount() const override { return 0; }
|
||||
QSizeF documentSize() const override { return QSizeF(); }
|
||||
QRectF frameBoundingRect(QTextFrame *) const override { return QRectF(); }
|
||||
QRectF blockBoundingRect(const QTextBlock &) const override { return QRectF(); }
|
||||
void documentChanged(int from, int /* oldLength */, int length) override
|
||||
{
|
||||
++changeEvents;
|
||||
|
||||
QTextBlock last = document()->lastBlock();
|
||||
int lastPos = last.position() + last.length() - 1;
|
||||
if (from == 0 && length == lastPos)
|
||||
gotFullLayout = true;
|
||||
}
|
||||
|
||||
bool gotFullLayout;
|
||||
int blockCount;
|
||||
int changeEvents;
|
||||
|
||||
public slots:
|
||||
void blockCountChanged(int bc) { blockCount = bc; }
|
||||
};
|
||||
|
||||
// Testing get/set functions
|
||||
void tst_QAbstractTextDocumentLayout::getSetCheck()
|
||||
{
|
||||
QTextDocument doc;
|
||||
MyAbstractTextDocumentLayout obj1(&doc);
|
||||
// QPaintDevice * QAbstractTextDocumentLayout::paintDevice()
|
||||
// void QAbstractTextDocumentLayout::setPaintDevice(QPaintDevice *)
|
||||
QImage *var1 = new QImage(QSize(10,10), QImage::Format_ARGB32_Premultiplied);
|
||||
obj1.setPaintDevice(var1);
|
||||
QCOMPARE(static_cast<QPaintDevice *>(var1), obj1.paintDevice());
|
||||
obj1.setPaintDevice((QPaintDevice *)0);
|
||||
QCOMPARE(static_cast<QPaintDevice *>(0), obj1.paintDevice());
|
||||
delete var1;
|
||||
}
|
||||
|
||||
void tst_QAbstractTextDocumentLayout::maximumBlockCount()
|
||||
{
|
||||
QTextDocument doc;
|
||||
doc.setMaximumBlockCount(10);
|
||||
|
||||
MyAbstractTextDocumentLayout layout(&doc);
|
||||
doc.setDocumentLayout(&layout);
|
||||
QObject::connect(&doc, SIGNAL(blockCountChanged(int)), &layout, SLOT(blockCountChanged(int)));
|
||||
|
||||
QTextCursor cursor(&doc);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("bla");
|
||||
}
|
||||
|
||||
QCOMPARE(layout.blockCount, 10);
|
||||
|
||||
layout.gotFullLayout = false;
|
||||
layout.changeEvents = 0;
|
||||
cursor.insertBlock();
|
||||
QCOMPARE(layout.changeEvents, 2);
|
||||
cursor.insertText("foo");
|
||||
QCOMPARE(layout.changeEvents, 3);
|
||||
cursor.insertBlock();
|
||||
QCOMPARE(layout.changeEvents, 5);
|
||||
cursor.insertText("foo");
|
||||
QCOMPARE(layout.changeEvents, 6);
|
||||
|
||||
QVERIFY(!layout.gotFullLayout);
|
||||
|
||||
QCOMPARE(layout.blockCount, 10);
|
||||
}
|
||||
|
||||
void tst_QAbstractTextDocumentLayout::anchorAt()
|
||||
{
|
||||
QTextDocument doc;
|
||||
doc.setHtml("<a href=\"link\">foo</a>");
|
||||
QAbstractTextDocumentLayout *documentLayout = doc.documentLayout();
|
||||
QTextBlock firstBlock = doc.begin();
|
||||
QTextLayout *layout = firstBlock.layout();
|
||||
layout->setPreeditArea(doc.toPlainText().size(), "xxx");
|
||||
|
||||
doc.setPageSize(QSizeF(1000, 1000));
|
||||
QFontMetrics metrics(layout->font());
|
||||
QPointF blockStart = documentLayout->blockBoundingRect(firstBlock).topLeft();
|
||||
|
||||
// anchorAt on start returns link
|
||||
QRect linkBr = metrics.boundingRect("foo");
|
||||
QPointF linkPoint((linkBr.width() / 2) + blockStart.x(), (linkBr.height() / 2) + blockStart.y());
|
||||
QCOMPARE(documentLayout->anchorAt(linkPoint), QString("link"));
|
||||
|
||||
// anchorAt() on top of preedit at end should not assert
|
||||
QRect preeditBr = metrics.boundingRect(doc.toPlainText() + "xx");
|
||||
QPointF preeditPoint(preeditBr.width() + blockStart.x(), (preeditBr.height() / 2) + blockStart.y());
|
||||
QCOMPARE(documentLayout->anchorAt(preeditPoint), QString());
|
||||
|
||||
// preedit at start should not return link
|
||||
layout->setPreeditArea(0, "xxx");
|
||||
preeditBr = metrics.boundingRect("xx");
|
||||
preeditPoint = QPointF(preeditBr.width() + blockStart.x(), (preeditBr.height() / 2) + blockStart.y());
|
||||
QCOMPARE(documentLayout->anchorAt(preeditPoint), QString());
|
||||
}
|
||||
|
||||
void tst_QAbstractTextDocumentLayout::imageAt()
|
||||
{
|
||||
QTextDocument doc;
|
||||
doc.setHtml("foo<a href=\"link\"><img src=\"image\" width=\"50\" height=\"50\"/></a>");
|
||||
QAbstractTextDocumentLayout *documentLayout = doc.documentLayout();
|
||||
QTextBlock firstBlock = doc.begin();
|
||||
QTextLayout *layout = firstBlock.layout();
|
||||
layout->setPreeditArea(doc.toPlainText().size(), "xxx");
|
||||
|
||||
doc.setPageSize(QSizeF(1000, 1000));
|
||||
QFontMetrics metrics(layout->font());
|
||||
QPointF blockStart = documentLayout->blockBoundingRect(firstBlock).topLeft();
|
||||
|
||||
QRect fooBr = metrics.boundingRect("foo");
|
||||
QPointF imagePoint(fooBr.width() + blockStart.x() + 25, blockStart.y() + 25);
|
||||
// imageAt on image returns source
|
||||
QCOMPARE(documentLayout->imageAt(imagePoint), QString("image"));
|
||||
// anchorAt on image returns link
|
||||
QCOMPARE(documentLayout->anchorAt(imagePoint), QString("link"));
|
||||
|
||||
// imageAt on start returns nothing (there's the "foo" text)
|
||||
QPointF fooPoint(blockStart.x() + (fooBr.width() / 2), (fooBr.height() / 2) + blockStart.y());
|
||||
QCOMPARE(documentLayout->imageAt(fooPoint), QString());
|
||||
}
|
||||
|
||||
void tst_QAbstractTextDocumentLayout::formatAt()
|
||||
{
|
||||
QTextDocument doc;
|
||||
doc.setHtml("foo<i><a href=\"link\"><img src=\"image\" width=\"50\" height=\"50\"/></a></i>");
|
||||
QAbstractTextDocumentLayout *documentLayout = doc.documentLayout();
|
||||
QTextBlock firstBlock = doc.begin();
|
||||
QTextLayout *layout = firstBlock.layout();
|
||||
layout->setPreeditArea(doc.toPlainText().size(), "xxx");
|
||||
|
||||
doc.setPageSize(QSizeF(1000, 1000));
|
||||
QFontMetrics metrics(layout->font());
|
||||
QPointF blockStart = documentLayout->blockBoundingRect(firstBlock).topLeft();
|
||||
|
||||
QRect fooBr = metrics.boundingRect("foo");
|
||||
QPointF imagePoint(fooBr.width() + blockStart.x() + 25, blockStart.y() + 25);
|
||||
|
||||
QTextFormat format = documentLayout->formatAt(imagePoint);
|
||||
QVERIFY(format.isCharFormat());
|
||||
QVERIFY(format.toCharFormat().isAnchor());
|
||||
QVERIFY(format.toCharFormat().fontItalic());
|
||||
QVERIFY(format.isImageFormat());
|
||||
|
||||
// move over the unformatted "foo" text)
|
||||
QPointF fooPoint(blockStart.x() + (fooBr.width() / 2), (fooBr.height() / 2) + blockStart.y());
|
||||
format = documentLayout->formatAt(fooPoint);
|
||||
QVERIFY(format.isCharFormat());
|
||||
QVERIFY(!format.toCharFormat().isAnchor());
|
||||
QVERIFY(!format.toCharFormat().fontItalic());
|
||||
QVERIFY(!format.isImageFormat());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QAbstractTextDocumentLayout)
|
||||
#include "tst_qabstracttextdocumentlayout.moc"
|
22
tests/auto/gui/text/qcssparser/CMakeLists.txt
Normal file
22
tests/auto/gui/text/qcssparser/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qcssparser Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
file(GLOB_RECURSE test_data
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
testdata/*
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qcssparser
|
||||
SOURCES
|
||||
tst_qcssparser.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::Xml
|
||||
TESTDATA ${test_data}
|
||||
)
|
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments/input
vendored
Normal file
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments/input
vendored
Normal file
@ -0,0 +1 @@
|
||||
/* let's see if comments actually work *//*foo*/ "it /*should be preserved \"in strings*/ though"
|
4
tests/auto/gui/text/qcssparser/testdata/scanner/comments/output
vendored
Normal file
4
tests/auto/gui/text/qcssparser/testdata/scanner/comments/output
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
S|/* let's see if comments actually work */
|
||||
S|/*foo*/
|
||||
S|
|
||||
STRING|"it /*should be preserved "in strings*/ though"
|
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments2/input
vendored
Normal file
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments2/input
vendored
Normal file
@ -0,0 +1 @@
|
||||
/*foo*/{/*foo*/+/*foo*/>/*foo*/,/*foo*/}/*foo*/-
|
12
tests/auto/gui/text/qcssparser/testdata/scanner/comments2/output
vendored
Normal file
12
tests/auto/gui/text/qcssparser/testdata/scanner/comments2/output
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
S|/*foo*/
|
||||
LBRACE|{
|
||||
S|/*foo*/
|
||||
PLUS|+
|
||||
S|/*foo*/
|
||||
GREATER|>
|
||||
S|/*foo*/
|
||||
COMMA|,
|
||||
S|/*foo*/
|
||||
RBRACE|}
|
||||
S|/*foo*/
|
||||
MINUS|-
|
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments3/input
vendored
Normal file
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments3/input
vendored
Normal file
@ -0,0 +1 @@
|
||||
url(/*comment*/"www.kde.org")
|
4
tests/auto/gui/text/qcssparser/testdata/scanner/comments3/output
vendored
Normal file
4
tests/auto/gui/text/qcssparser/testdata/scanner/comments3/output
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
FUNCTION|url(
|
||||
S|/*comment*/
|
||||
STRING|"www.kde.org"
|
||||
RPAREN|)
|
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments4/input
vendored
Normal file
1
tests/auto/gui/text/qcssparser/testdata/scanner/comments4/input
vendored
Normal file
@ -0,0 +1 @@
|
||||
!/*hmm*/important
|
3
tests/auto/gui/text/qcssparser/testdata/scanner/comments4/output
vendored
Normal file
3
tests/auto/gui/text/qcssparser/testdata/scanner/comments4/output
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
EXCLAMATION_SYM|!
|
||||
S|/*hmm*/
|
||||
IDENT|important
|
1
tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/input
vendored
Normal file
1
tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/input
vendored
Normal file
@ -0,0 +1 @@
|
||||
background: 'test_bug.png';
|
5
tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/output
vendored
Normal file
5
tests/auto/gui/text/qcssparser/testdata/scanner/quotedstring/output
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
IDENT|background
|
||||
COLON|:
|
||||
S|
|
||||
STRING|'test_bug.png'
|
||||
SEMICOLON|;
|
1
tests/auto/gui/text/qcssparser/testdata/scanner/simple/input
vendored
Normal file
1
tests/auto/gui/text/qcssparser/testdata/scanner/simple/input
vendored
Normal file
@ -0,0 +1 @@
|
||||
p { display:block; }
|
9
tests/auto/gui/text/qcssparser/testdata/scanner/simple/output
vendored
Normal file
9
tests/auto/gui/text/qcssparser/testdata/scanner/simple/output
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
IDENT|p
|
||||
LBRACE| {
|
||||
S|
|
||||
IDENT|display
|
||||
COLON|:
|
||||
IDENT|block
|
||||
SEMICOLON|;
|
||||
S|
|
||||
RBRACE|}
|
1
tests/auto/gui/text/qcssparser/testdata/scanner/unicode/input
vendored
Normal file
1
tests/auto/gui/text/qcssparser/testdata/scanner/unicode/input
vendored
Normal file
@ -0,0 +1 @@
|
||||
\41"\7E"\00006Df
|
3
tests/auto/gui/text/qcssparser/testdata/scanner/unicode/output
vendored
Normal file
3
tests/auto/gui/text/qcssparser/testdata/scanner/unicode/output
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
IDENT|A
|
||||
STRING|"~"
|
||||
IDENT|mf
|
1756
tests/auto/gui/text/qcssparser/tst_qcssparser.cpp
Normal file
1756
tests/auto/gui/text/qcssparser/tst_qcssparser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
8
tests/auto/gui/text/qfont/BLACKLIST
Normal file
8
tests/auto/gui/text/qfont/BLACKLIST
Normal file
@ -0,0 +1,8 @@
|
||||
[defaultFamily:cursive]
|
||||
centos
|
||||
b2qt
|
||||
rhel
|
||||
[defaultFamily:fantasy]
|
||||
centos
|
||||
b2qt
|
||||
rhel
|
32
tests/auto/gui/text/qfont/CMakeLists.txt
Normal file
32
tests/auto/gui/text/qfont/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qfont Test:
|
||||
#####################################################################
|
||||
|
||||
# Resources:
|
||||
set(testfont_resource_files
|
||||
"datastream.515"
|
||||
"weirdfont.otf"
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qfont
|
||||
SOURCES
|
||||
tst_qfont.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::TestPrivate
|
||||
TESTDATA ${testfont_resource_files}
|
||||
BUILTIN_TESTDATA
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qfont CONDITION TARGET Qt::Widgets
|
||||
LIBRARIES
|
||||
Qt::Widgets
|
||||
)
|
BIN
tests/auto/gui/text/qfont/datastream.515
Normal file
BIN
tests/auto/gui/text/qfont/datastream.515
Normal file
Binary file not shown.
845
tests/auto/gui/text/qfont/tst_qfont.cpp
Normal file
845
tests/auto/gui/text/qfont/tst_qfont.cpp
Normal file
@ -0,0 +1,845 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#include <QBuffer>
|
||||
#include <QtEndian>
|
||||
#if QT_CONFIG(process)
|
||||
#include <QProcess>
|
||||
#endif
|
||||
|
||||
#include <qfont.h>
|
||||
#include <private/qfont_p.h>
|
||||
#include <qfontdatabase.h>
|
||||
#include <qfontinfo.h>
|
||||
#include <qstringlist.h>
|
||||
#include <qguiapplication.h>
|
||||
#ifndef QT_NO_WIDGETS
|
||||
#include <qwidget.h>
|
||||
#endif
|
||||
#include <qlist.h>
|
||||
#include <QtTest/private/qemulationdetector_p.h>
|
||||
|
||||
class tst_QFont : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void getSetCheck();
|
||||
void exactMatch();
|
||||
void compare();
|
||||
void resolve();
|
||||
#ifndef QT_NO_WIDGETS
|
||||
void resetFont();
|
||||
#endif
|
||||
void isCopyOf();
|
||||
void italicOblique();
|
||||
void insertAndRemoveSubstitutions();
|
||||
void serialize_data();
|
||||
void serialize();
|
||||
void deserializeQt515();
|
||||
|
||||
void styleName();
|
||||
void defaultFamily_data();
|
||||
void defaultFamily();
|
||||
void toAndFromString();
|
||||
void fromStringWithoutStyleName();
|
||||
void fromDegenerateString_data();
|
||||
void fromDegenerateString();
|
||||
|
||||
void sharing();
|
||||
void familyNameWithCommaQuote_data();
|
||||
void familyNameWithCommaQuote();
|
||||
void setFamilies_data();
|
||||
void setFamilies();
|
||||
void setFamiliesAndFamily_data();
|
||||
void setFamiliesAndFamily();
|
||||
};
|
||||
|
||||
// Testing get/set functions
|
||||
void tst_QFont::getSetCheck()
|
||||
{
|
||||
QFont obj1;
|
||||
// Style QFont::style()
|
||||
// void QFont::setStyle(Style)
|
||||
obj1.setStyle(QFont::Style(QFont::StyleNormal));
|
||||
QCOMPARE(QFont::Style(QFont::StyleNormal), obj1.style());
|
||||
obj1.setStyle(QFont::Style(QFont::StyleItalic));
|
||||
QCOMPARE(QFont::Style(QFont::StyleItalic), obj1.style());
|
||||
obj1.setStyle(QFont::Style(QFont::StyleOblique));
|
||||
QCOMPARE(QFont::Style(QFont::StyleOblique), obj1.style());
|
||||
|
||||
// StyleStrategy QFont::styleStrategy()
|
||||
// void QFont::setStyleStrategy(StyleStrategy)
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDefault));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::PreferDefault), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferBitmap));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::PreferBitmap), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferDevice));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::PreferDevice), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferOutline));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::PreferOutline), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::ForceOutline));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::ForceOutline), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferMatch));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::PreferMatch), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferQuality));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::PreferQuality), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::PreferAntialias));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::PreferAntialias), obj1.styleStrategy());
|
||||
obj1.setStyleStrategy(QFont::StyleStrategy(QFont::NoAntialias));
|
||||
QCOMPARE(QFont::StyleStrategy(QFont::NoAntialias), obj1.styleStrategy());
|
||||
}
|
||||
|
||||
void tst_QFont::exactMatch()
|
||||
{
|
||||
QFont font;
|
||||
|
||||
// Check if a non-existing font hasn't an exact match
|
||||
font = QFont( "BogusFont", 33 );
|
||||
QVERIFY( !font.exactMatch() );
|
||||
QVERIFY(!QFont("sans").exactMatch());
|
||||
QVERIFY(!QFont("sans-serif").exactMatch());
|
||||
QVERIFY(!QFont("serif").exactMatch());
|
||||
QVERIFY(!QFont("monospace").exactMatch());
|
||||
|
||||
font.setFamilies(QStringList() << "BogusFont");
|
||||
QVERIFY(!font.exactMatch());
|
||||
QVERIFY(!QFont("sans").exactMatch());
|
||||
QVERIFY(!QFont("sans-serif").exactMatch());
|
||||
QVERIFY(!QFont("serif").exactMatch());
|
||||
QVERIFY(!QFont("monospace").exactMatch());
|
||||
|
||||
// Confirm that exactMatch is true for a valid font
|
||||
const QString family = QFontDatabase::families().first();
|
||||
const QString style = QFontDatabase::styles(family).first();
|
||||
const int pointSize = QFontDatabase::pointSizes(family, style).first();
|
||||
font = QFontDatabase::font(family, style, pointSize);
|
||||
QVERIFY(font.exactMatch());
|
||||
|
||||
if (QFontDatabase::families().contains("Arial")) {
|
||||
font = QFont("Arial");
|
||||
QVERIFY(font.exactMatch());
|
||||
font = QFont(QString());
|
||||
font.setFamilies({"Arial"});
|
||||
QVERIFY(font.exactMatch());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFont::italicOblique()
|
||||
{
|
||||
QStringList families = QFontDatabase::families();
|
||||
if (families.isEmpty())
|
||||
return;
|
||||
|
||||
QStringList::ConstIterator f_it, f_end = families.end();
|
||||
for (f_it = families.begin(); f_it != f_end; ++f_it) {
|
||||
|
||||
QString family = *f_it;
|
||||
QStringList styles = QFontDatabase::styles(family);
|
||||
QStringList::ConstIterator s_it, s_end = styles.end();
|
||||
for (s_it = styles.begin(); s_it != s_end; ++s_it) {
|
||||
QString style = *s_it;
|
||||
|
||||
if (QFontDatabase::isSmoothlyScalable(family, style)) {
|
||||
if (style.contains("Oblique")) {
|
||||
style.replace("Oblique", "Italic");
|
||||
} else if (style.contains("Italic")) {
|
||||
style.replace("Italic", "Oblique");
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
QFont f = QFontDatabase::font(family, style, 12);
|
||||
QVERIFY(f.italic());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFont::compare()
|
||||
{
|
||||
QFont font;
|
||||
{
|
||||
QFont font2 = font;
|
||||
font2.setPointSize(24);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
}
|
||||
{
|
||||
QFont font2 = font;
|
||||
font2.setPixelSize(24);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
}
|
||||
|
||||
font.setPointSize(12);
|
||||
font.setItalic(false);
|
||||
font.setWeight(QFont::Normal);
|
||||
font.setUnderline(false);
|
||||
font.setStrikeOut(false);
|
||||
font.setOverline(false);
|
||||
{
|
||||
QFont font2 = font;
|
||||
font2.setPointSize(24);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
}
|
||||
{
|
||||
QFont font2 = font;
|
||||
font2.setPixelSize(24);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
}
|
||||
{
|
||||
QFont font2 = font;
|
||||
|
||||
font2.setItalic(true);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
font2.setItalic(false);
|
||||
QCOMPARE(font, font2);
|
||||
QVERIFY(!(font < font2));
|
||||
|
||||
font2.setWeight(QFont::Bold);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
font2.setWeight(QFont::Normal);
|
||||
QCOMPARE(font, font2);
|
||||
QVERIFY(!(font < font2));
|
||||
|
||||
font.setUnderline(true);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
font.setUnderline(false);
|
||||
QCOMPARE(font, font2);
|
||||
QVERIFY(!(font < font2));
|
||||
|
||||
font.setStrikeOut(true);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
font.setStrikeOut(false);
|
||||
QCOMPARE(font, font2);
|
||||
QVERIFY(!(font < font2));
|
||||
|
||||
font.setOverline(true);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
font.setOverline(false);
|
||||
QCOMPARE(font, font2);
|
||||
QVERIFY(!(font < font2));
|
||||
|
||||
font.setCapitalization(QFont::SmallCaps);
|
||||
QVERIFY(font != font2);
|
||||
QCOMPARE(font < font2,!(font2 < font));
|
||||
font.setCapitalization(QFont::MixedCase);
|
||||
QCOMPARE(font, font2);
|
||||
QVERIFY(!(font < font2));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFont::resolve()
|
||||
{
|
||||
QFont font;
|
||||
font.setPointSize(font.pointSize() * 2);
|
||||
font.setItalic(false);
|
||||
font.setWeight(QFont::Normal);
|
||||
font.setUnderline(false);
|
||||
font.setStrikeOut(false);
|
||||
font.setOverline(false);
|
||||
font.setStretch(QFont::Unstretched);
|
||||
|
||||
QFont font1;
|
||||
font1.setWeight(QFont::Bold);
|
||||
QFont font2 = font1.resolve(font);
|
||||
|
||||
QCOMPARE(font2.weight(), font1.weight());
|
||||
|
||||
QCOMPARE(font2.pointSize(), font.pointSize());
|
||||
QCOMPARE(font2.italic(), font.italic());
|
||||
QCOMPARE(font2.underline(), font.underline());
|
||||
QCOMPARE(font2.overline(), font.overline());
|
||||
QCOMPARE(font2.strikeOut(), font.strikeOut());
|
||||
QCOMPARE(font2.stretch(), font.stretch());
|
||||
|
||||
QFont font3;
|
||||
font3.setStretch(QFont::UltraCondensed);
|
||||
QFont font4 = font3.resolve(font1).resolve(font);
|
||||
|
||||
QCOMPARE(font4.stretch(), font3.stretch());
|
||||
|
||||
QCOMPARE(font4.weight(), font.weight());
|
||||
QCOMPARE(font4.pointSize(), font.pointSize());
|
||||
QCOMPARE(font4.italic(), font.italic());
|
||||
QCOMPARE(font4.underline(), font.underline());
|
||||
QCOMPARE(font4.overline(), font.overline());
|
||||
QCOMPARE(font4.strikeOut(), font.strikeOut());
|
||||
|
||||
|
||||
QFont f1,f2,f3;
|
||||
f2.setPointSize(45);
|
||||
f3.setPointSize(55);
|
||||
|
||||
QFont f4 = f1.resolve(f2);
|
||||
QCOMPARE(f4.pointSize(), 45);
|
||||
f4 = f4.resolve(f3);
|
||||
QCOMPARE(f4.pointSize(), 55);
|
||||
|
||||
QFont font5, font6;
|
||||
const QStringList fontFamilies = { QStringLiteral("Arial") };
|
||||
font5.setFamilies(fontFamilies);
|
||||
font6 = font6.resolve(font5);
|
||||
QCOMPARE(font6.families(), fontFamilies);
|
||||
|
||||
QFont font7, font8;
|
||||
// This will call setFamilies() directly now
|
||||
font7.setFamily(QLatin1String("Helvetica"));
|
||||
font8.setFamilies(fontFamilies);
|
||||
font7 = font7.resolve(font8);
|
||||
QCOMPARE(font7.families(), QStringList({"Helvetica"}));
|
||||
QCOMPARE(font7.family(), "Helvetica");
|
||||
}
|
||||
|
||||
#ifndef QT_NO_WIDGETS
|
||||
void tst_QFont::resetFont()
|
||||
{
|
||||
QWidget parent;
|
||||
QWidget firstChild(&parent);
|
||||
QFont parentFont = parent.font();
|
||||
parentFont.setPointSize(parentFont.pointSize() + 2);
|
||||
parent.setFont(parentFont);
|
||||
|
||||
QFont childFont = firstChild.font();
|
||||
childFont.setBold(!childFont.bold());
|
||||
firstChild.setFont(childFont);
|
||||
|
||||
QWidget secondChild(&parent);
|
||||
secondChild.setFont(childFont);
|
||||
|
||||
QVERIFY(parentFont.resolveMask() != 0);
|
||||
QVERIFY(childFont.resolveMask() != 0);
|
||||
QVERIFY(childFont != parentFont);
|
||||
|
||||
// reset font on both children
|
||||
firstChild.setFont(QFont());
|
||||
secondChild.setFont(QFont());
|
||||
|
||||
QCOMPARE(firstChild.font().resolveMask(), QFont::SizeResolved);
|
||||
QCOMPARE(secondChild.font().resolveMask(), QFont::SizeResolved);
|
||||
QCOMPARE(firstChild.font().pointSize(), parent.font().pointSize());
|
||||
QCOMPARE(secondChild.font().pointSize(), parent.font().pointSize());
|
||||
QVERIFY(parent.font().resolveMask() != 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QFont::isCopyOf()
|
||||
{
|
||||
QFont font;
|
||||
QVERIFY(font.isCopyOf(QGuiApplication::font()));
|
||||
|
||||
QFont font2("bogusfont", 23);
|
||||
QVERIFY(! font2.isCopyOf(QGuiApplication::font()));
|
||||
|
||||
QFont font3 = font;
|
||||
QVERIFY(font3.isCopyOf(font));
|
||||
|
||||
font3.setPointSize(256);
|
||||
QVERIFY(!font3.isCopyOf(font));
|
||||
font3.setPointSize(font.pointSize());
|
||||
QVERIFY(!font3.isCopyOf(font));
|
||||
}
|
||||
|
||||
void tst_QFont::insertAndRemoveSubstitutions()
|
||||
{
|
||||
QFont::removeSubstitutions("BogusFontFamily");
|
||||
// make sure it is empty before we start
|
||||
QVERIFY(QFont::substitutes("BogusFontFamily").isEmpty());
|
||||
QVERIFY(QFont::substitutes("bogusfontfamily").isEmpty());
|
||||
|
||||
// inserting Foo
|
||||
QFont::insertSubstitution("BogusFontFamily", "Foo");
|
||||
QCOMPARE(QFont::substitutes("BogusFontFamily").size(), 1);
|
||||
QCOMPARE(QFont::substitutes("bogusfontfamily").size(), 1);
|
||||
|
||||
// inserting Bar and Baz
|
||||
QStringList moreFonts;
|
||||
moreFonts << "Bar" << "Baz";
|
||||
QFont::insertSubstitutions("BogusFontFamily", moreFonts);
|
||||
QCOMPARE(QFont::substitutes("BogusFontFamily").size(), 3);
|
||||
QCOMPARE(QFont::substitutes("bogusfontfamily").size(), 3);
|
||||
|
||||
QFont::removeSubstitutions("BogusFontFamily");
|
||||
// make sure it is empty again
|
||||
QVERIFY(QFont::substitutes("BogusFontFamily").isEmpty());
|
||||
QVERIFY(QFont::substitutes("bogusfontfamily").isEmpty());
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QDataStream::Version)
|
||||
|
||||
void tst_QFont::serialize_data()
|
||||
{
|
||||
QTest::addColumn<QFont>("font");
|
||||
// The version in which the tested feature was added.
|
||||
QTest::addColumn<QDataStream::Version>("minimumStreamVersion");
|
||||
|
||||
QFont basicFont;
|
||||
// Versions <= Qt 2.1 had broken point size serialization,
|
||||
// so we set an integer point size.
|
||||
basicFont.setPointSize(9);
|
||||
// Versions <= Qt 5.4 didn't serialize styleName, so clear it
|
||||
basicFont.setStyleName(QString());
|
||||
|
||||
QFont font = basicFont;
|
||||
QTest::newRow("defaultConstructed") << font << QDataStream::Qt_1_0;
|
||||
|
||||
font.setLetterSpacing(QFont::AbsoluteSpacing, 105);
|
||||
QTest::newRow("letterSpacing=105") << font << QDataStream::Qt_4_5;
|
||||
|
||||
font = basicFont;
|
||||
font.setWordSpacing(50.0);
|
||||
QTest::newRow("wordSpacing") << font << QDataStream::Qt_4_5;
|
||||
|
||||
font = basicFont;
|
||||
font.setPointSize(20);
|
||||
QTest::newRow("pointSize") << font << QDataStream::Qt_1_0;
|
||||
|
||||
font = basicFont;
|
||||
font.setPixelSize(32);
|
||||
QTest::newRow("pixelSize") << font << QDataStream::Qt_3_0;
|
||||
|
||||
font = basicFont;
|
||||
font.setStyleHint(QFont::Monospace);
|
||||
QTest::newRow("styleHint") << font << QDataStream::Qt_1_0;
|
||||
|
||||
font = basicFont;
|
||||
font.setStretch(4000);
|
||||
QTest::newRow("stretch") << font << QDataStream::Qt_4_3;
|
||||
|
||||
font = basicFont;
|
||||
font.setWeight(QFont::Light);
|
||||
QTest::newRow("weight") << font << QDataStream::Qt_1_0;
|
||||
|
||||
font = basicFont;
|
||||
font.setUnderline(true);
|
||||
QTest::newRow("underline") << font << QDataStream::Qt_1_0;
|
||||
|
||||
font = basicFont;
|
||||
font.setStrikeOut(true);
|
||||
QTest::newRow("strikeOut") << font << QDataStream::Qt_1_0;
|
||||
|
||||
font = basicFont;
|
||||
font.setFixedPitch(true);
|
||||
// This fails for versions less than this, as ignorePitch is set to false
|
||||
// whenever setFixedPitch() is called, but ignorePitch is considered an
|
||||
// extended bit, which were apparently not available until 4.4.
|
||||
QTest::newRow("fixedPitch") << font << QDataStream::Qt_4_4;
|
||||
|
||||
font = basicFont;
|
||||
font.setLetterSpacing(QFont::AbsoluteSpacing, 10);
|
||||
// Fails for 4.4 because letterSpacing wasn't read until 4.5.
|
||||
QTest::newRow("letterSpacing=10") << font << QDataStream::Qt_4_5;
|
||||
|
||||
font = basicFont;
|
||||
font.setKerning(false);
|
||||
QTest::newRow("kerning") << font << QDataStream::Qt_4_0;
|
||||
|
||||
font = basicFont;
|
||||
font.setStyleStrategy(QFont::NoFontMerging);
|
||||
// This wasn't read properly until 5.4.
|
||||
QTest::newRow("styleStrategy") << font << QDataStream::Qt_5_4;
|
||||
|
||||
font = basicFont;
|
||||
font.setHintingPreference(QFont::PreferFullHinting);
|
||||
// This wasn't read until 5.4.
|
||||
QTest::newRow("hintingPreference") << font << QDataStream::Qt_5_4;
|
||||
|
||||
font = basicFont;
|
||||
font.setStyleName("Regular Black Condensed");
|
||||
// This wasn't read until 5.4.
|
||||
QTest::newRow("styleName") << font << QDataStream::Qt_5_4;
|
||||
|
||||
font = basicFont;
|
||||
font.setCapitalization(QFont::AllUppercase);
|
||||
// This wasn't read until 5.6.
|
||||
QTest::newRow("capitalization") << font << QDataStream::Qt_5_6;
|
||||
}
|
||||
|
||||
void tst_QFont::serialize()
|
||||
{
|
||||
QFETCH(QFont, font);
|
||||
QFETCH(QDataStream::Version, minimumStreamVersion);
|
||||
|
||||
QDataStream stream;
|
||||
const int thisVersion = stream.version();
|
||||
|
||||
for (int version = minimumStreamVersion; version <= thisVersion; ++version) {
|
||||
QBuffer buffer;
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
stream.setDevice(&buffer);
|
||||
stream.setVersion(version);
|
||||
stream << font;
|
||||
buffer.close();
|
||||
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QFont readFont;
|
||||
stream >> readFont;
|
||||
QVERIFY2(readFont == font, qPrintable(QString::fromLatin1("Fonts do not compare equal for QDataStream version ") +
|
||||
QString::fromLatin1("%1:\nactual: %2\nexpected: %3").arg(version).arg(readFont.toString()).arg(font.toString())));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFont::deserializeQt515()
|
||||
{
|
||||
QFile file;
|
||||
file.setFileName(QFINDTESTDATA("datastream.515"));
|
||||
QVERIFY(file.open(QIODevice::ReadOnly));
|
||||
|
||||
QFont font;
|
||||
{
|
||||
QDataStream stream(&file);
|
||||
stream.setVersion(QDataStream::Qt_5_15);
|
||||
stream >> font;
|
||||
}
|
||||
|
||||
QCOMPARE(font.family(), QStringLiteral("FirstFamily"));
|
||||
QCOMPARE(font.families().size(), 3);
|
||||
QCOMPARE(font.families().at(0), QStringLiteral("FirstFamily"));
|
||||
QCOMPARE(font.families().at(1), QStringLiteral("OtherFamily1"));
|
||||
QCOMPARE(font.families().at(2), QStringLiteral("OtherFamily2"));
|
||||
QCOMPARE(font.pointSize(), 12);
|
||||
|
||||
QVERIFY(file.reset());
|
||||
QByteArray fileContent = file.readAll();
|
||||
QByteArray serializedContent;
|
||||
{
|
||||
QBuffer buffer(&serializedContent);
|
||||
QVERIFY(buffer.open(QIODevice::WriteOnly));
|
||||
|
||||
QDataStream stream(&buffer);
|
||||
stream.setVersion(QDataStream::Qt_5_15);
|
||||
stream << font;
|
||||
}
|
||||
|
||||
QCOMPARE(serializedContent, fileContent);
|
||||
|
||||
file.close();
|
||||
}
|
||||
|
||||
void tst_QFont::styleName()
|
||||
{
|
||||
#if !defined(Q_OS_MAC)
|
||||
QSKIP("Only tested on Mac");
|
||||
#else
|
||||
QFont font("Helvetica Neue");
|
||||
font.setStyleName("UltraLight");
|
||||
|
||||
QCOMPARE(QFontInfo(font).styleName(), QString("UltraLight"));
|
||||
#endif
|
||||
}
|
||||
|
||||
QString getPlatformGenericFont(const char* genericName)
|
||||
{
|
||||
#if defined(Q_OS_UNIX) && !defined(QT_NO_FONTCONFIG) && QT_CONFIG(process)
|
||||
QProcess p;
|
||||
p.start(QLatin1String("fc-match"), (QStringList() << "-f%{family}" << genericName));
|
||||
if (!p.waitForStarted())
|
||||
qWarning("fc-match cannot be started: %s", qPrintable(p.errorString()));
|
||||
if (p.waitForFinished())
|
||||
return QString::fromLatin1(p.readAllStandardOutput());
|
||||
#endif
|
||||
return QLatin1String(genericName);
|
||||
}
|
||||
|
||||
static inline QByteArray msgNotAcceptableFont(const QString &defaultFamily, const QStringList &acceptableFamilies)
|
||||
{
|
||||
QString res = QString::fromLatin1("Font family '%1' is not one of the following acceptable results: ").arg(defaultFamily);
|
||||
Q_FOREACH (const QString &family, acceptableFamilies)
|
||||
res += QLatin1String("\n ") + family;
|
||||
return res.toLocal8Bit();
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QFont::StyleHint)
|
||||
void tst_QFont::defaultFamily_data()
|
||||
{
|
||||
QTest::addColumn<QFont::StyleHint>("styleHint");
|
||||
QTest::addColumn<QStringList>("acceptableFamilies");
|
||||
|
||||
QTest::newRow("serif") << QFont::Serif << (QStringList() << "Times New Roman" << "Times" << "Droid Serif" << getPlatformGenericFont("serif").split(","));
|
||||
QTest::newRow("monospace") << QFont::Monospace << (QStringList() << "Courier New" << "Monaco" << "Menlo" << "Droid Sans Mono" << getPlatformGenericFont("monospace").split(","));
|
||||
QTest::newRow("cursive") << QFont::Cursive << (QStringList() << "Comic Sans MS" << "Apple Chancery" << "Roboto" << "Droid Sans" << getPlatformGenericFont("cursive").split(","));
|
||||
QTest::newRow("fantasy") << QFont::Fantasy << (QStringList() << "Impact" << "Zapfino" << "Roboto" << "Droid Sans" << getPlatformGenericFont("fantasy").split(","));
|
||||
QTest::newRow("sans-serif") << QFont::SansSerif << (QStringList() << "Arial" << "Lucida Grande" << "Helvetica" << "Roboto" << "Droid Sans" << "Segoe UI" << getPlatformGenericFont("sans-serif").split(","));
|
||||
}
|
||||
|
||||
void tst_QFont::defaultFamily()
|
||||
{
|
||||
QFETCH(QFont::StyleHint, styleHint);
|
||||
QFETCH(QStringList, acceptableFamilies);
|
||||
|
||||
QFont f;
|
||||
f.setStyleHint(styleHint);
|
||||
const QString familyForHint(f.defaultFamily());
|
||||
|
||||
// it should at least return a family that is available.
|
||||
QVERIFY(QFontDatabase::hasFamily(familyForHint));
|
||||
|
||||
bool isAcceptable = false;
|
||||
Q_FOREACH (const QString& family, acceptableFamilies) {
|
||||
if (!familyForHint.compare(family, Qt::CaseInsensitive)) {
|
||||
isAcceptable = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(Q_OS_UNIX) && defined(QT_NO_FONTCONFIG)
|
||||
QSKIP("This platform does not support checking for default font acceptability");
|
||||
#endif
|
||||
|
||||
#ifdef Q_PROCESSOR_ARM_32
|
||||
if (QTestPrivate::isRunningArmOnX86())
|
||||
QEXPECT_FAIL("", "Fails on ARMv7 QEMU (QTQAINFRA-4127)", Continue);
|
||||
#endif
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
QEXPECT_FAIL("serif", "QTBUG-69215", Continue);
|
||||
#endif
|
||||
QVERIFY2(isAcceptable, msgNotAcceptableFont(familyForHint, acceptableFamilies));
|
||||
}
|
||||
|
||||
void tst_QFont::toAndFromString()
|
||||
{
|
||||
QFont defaultFont = QGuiApplication::font();
|
||||
QString family = defaultFont.family();
|
||||
|
||||
const QStringList stylesList = QFontDatabase::styles(family);
|
||||
if (stylesList.size() == 0)
|
||||
QSKIP("Default font doesn't have any styles");
|
||||
|
||||
for (const QString &style : stylesList) {
|
||||
QFont result;
|
||||
QFont initial = QFontDatabase::font(family, style, defaultFont.pointSize());
|
||||
|
||||
result.fromString(initial.toString());
|
||||
|
||||
QCOMPARE(result, initial);
|
||||
}
|
||||
|
||||
// Since Qt 6.0 it was changed to include more information in the description, so
|
||||
// this checks for compatibility
|
||||
const QString fontStringFrom515(QLatin1String("Times New Roman,18,-1,5,75,1,0,0,1,0,Regular"));
|
||||
QFont fontFrom515("Times New Roman", 18);
|
||||
fontFrom515.setBold(true);
|
||||
fontFrom515.setItalic(true);
|
||||
fontFrom515.setFixedPitch(true);
|
||||
fontFrom515.setStyleName("Regular");
|
||||
QFont from515String;
|
||||
from515String.fromString(fontStringFrom515);
|
||||
QCOMPARE(from515String, fontFrom515);
|
||||
|
||||
const QString fontStringFrom60(
|
||||
QLatin1String("Times New Roman,18,-1,5,700,1,0,0,1,0,1,0,150.5,2.5,50,2,Regular"));
|
||||
QFont fontFrom60 = fontFrom515;
|
||||
fontFrom60.setStyleStrategy(QFont::PreferBitmap);
|
||||
fontFrom60.setCapitalization(QFont::AllUppercase);
|
||||
fontFrom60.setLetterSpacing(QFont::PercentageSpacing, 150.5);
|
||||
fontFrom60.setWordSpacing(2.5);
|
||||
fontFrom60.setStretch(50);
|
||||
QFont from60String;
|
||||
from60String.fromString(fontStringFrom60);
|
||||
QCOMPARE(fontFrom60.toString(), fontStringFrom60);
|
||||
QCOMPARE(from60String, fontFrom60);
|
||||
}
|
||||
|
||||
void tst_QFont::fromStringWithoutStyleName()
|
||||
{
|
||||
QFont font1;
|
||||
font1.fromString("Noto Sans,12,-1,5,50,0,0,0,0,0,Regular");
|
||||
|
||||
QFont font2 = font1;
|
||||
const QString str = "Times,16,-1,5,400,0,0,0,0,0,0,0,0,0,0,1";
|
||||
font2.fromString(str);
|
||||
|
||||
QCOMPARE(font2.toString(), str);
|
||||
|
||||
const QString fontStringFrom60(
|
||||
QLatin1String("Times New Roman,18,-1,5,700,1,0,0,1,0,1,0,150.5,2.5,50,2"));
|
||||
QFont font3;
|
||||
font3.fromString("Noto Sans,12,-1,5,50,0,0,0,0,0,Regular");
|
||||
QFont font4 = font3;
|
||||
font4.fromString(fontStringFrom60);
|
||||
QCOMPARE(font4.toString(), fontStringFrom60);
|
||||
}
|
||||
|
||||
void tst_QFont::fromDegenerateString_data()
|
||||
{
|
||||
QTest::addColumn<QString>("string");
|
||||
|
||||
QTest::newRow("empty") << QString();
|
||||
QTest::newRow("justAComma") << ",";
|
||||
QTest::newRow("commasAndSpaces") << " , , ";
|
||||
QTest::newRow("spaces") << " ";
|
||||
QTest::newRow("spacesTabsAndNewlines") << " \t \n";
|
||||
}
|
||||
|
||||
void tst_QFont::fromDegenerateString()
|
||||
{
|
||||
QFETCH(QString, string);
|
||||
QFont f;
|
||||
QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*Invalid description.*"));
|
||||
QCOMPARE(f.fromString(string), false);
|
||||
QCOMPARE(f, QFont());
|
||||
}
|
||||
|
||||
void tst_QFont::sharing()
|
||||
{
|
||||
// QFontCache references the engineData
|
||||
int refs_by_cache = 1;
|
||||
|
||||
QFont f;
|
||||
f.setStyleHint(QFont::Serif);
|
||||
f.exactMatch(); // loads engine
|
||||
QCOMPARE(QFontPrivate::get(f)->ref.loadRelaxed(), 1);
|
||||
QVERIFY(QFontPrivate::get(f)->engineData);
|
||||
QCOMPARE(QFontPrivate::get(f)->engineData->ref.loadRelaxed(), 1 + refs_by_cache);
|
||||
|
||||
QFont f2(f);
|
||||
QCOMPARE(QFontPrivate::get(f2), QFontPrivate::get(f));
|
||||
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 2);
|
||||
QVERIFY(QFontPrivate::get(f2)->engineData);
|
||||
QCOMPARE(QFontPrivate::get(f2)->engineData, QFontPrivate::get(f)->engineData);
|
||||
QCOMPARE(QFontPrivate::get(f2)->engineData->ref.loadRelaxed(), 1 + refs_by_cache);
|
||||
|
||||
f2.setKerning(!f.kerning());
|
||||
QVERIFY(QFontPrivate::get(f2) != QFontPrivate::get(f));
|
||||
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 1);
|
||||
QVERIFY(QFontPrivate::get(f2)->engineData);
|
||||
QCOMPARE(QFontPrivate::get(f2)->engineData, QFontPrivate::get(f)->engineData);
|
||||
QCOMPARE(QFontPrivate::get(f2)->engineData->ref.loadRelaxed(), 2 + refs_by_cache);
|
||||
|
||||
f2 = f;
|
||||
QCOMPARE(QFontPrivate::get(f2), QFontPrivate::get(f));
|
||||
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 2);
|
||||
QVERIFY(QFontPrivate::get(f2)->engineData);
|
||||
QCOMPARE(QFontPrivate::get(f2)->engineData, QFontPrivate::get(f)->engineData);
|
||||
QCOMPARE(QFontPrivate::get(f2)->engineData->ref.loadRelaxed(), 1 + refs_by_cache);
|
||||
|
||||
if (f.pointSize() > 0)
|
||||
f2.setPointSize(f.pointSize() * 2 / 3);
|
||||
else
|
||||
f2.setPixelSize(f.pixelSize() * 2 / 3);
|
||||
QVERIFY(QFontPrivate::get(f2) != QFontPrivate::get(f));
|
||||
QCOMPARE(QFontPrivate::get(f2)->ref.loadRelaxed(), 1);
|
||||
QVERIFY(!QFontPrivate::get(f2)->engineData);
|
||||
QVERIFY(QFontPrivate::get(f2)->engineData != QFontPrivate::get(f)->engineData);
|
||||
}
|
||||
|
||||
void tst_QFont::familyNameWithCommaQuote_data()
|
||||
{
|
||||
QTest::addColumn<QString>("enteredFamilyName");
|
||||
QTest::addColumn<QString>("familyName");
|
||||
QTest::addColumn<QString>("chosenFamilyName");
|
||||
|
||||
const QString standardFont(QFont().defaultFamily());
|
||||
if (standardFont.isEmpty())
|
||||
QSKIP("No default font available on the system");
|
||||
const QString weirdFont(QLatin1String("'My, weird'' font name',"));
|
||||
const QString bogusFont(QLatin1String("BogusFont"));
|
||||
const QString commaSeparated(standardFont + QLatin1String(",Times New Roman"));
|
||||
const QString commaSeparatedWeird(weirdFont + QLatin1String(",") + standardFont);
|
||||
const QString commaSeparatedBogus(bogusFont + QLatin1String(",") + standardFont);
|
||||
|
||||
QTest::newRow("standard") << standardFont << standardFont << standardFont;
|
||||
QTest::newRow("weird") << weirdFont << QString("'My") << standardFont;
|
||||
QTest::newRow("commaSeparated") << commaSeparated << standardFont << standardFont;
|
||||
QTest::newRow("commaSeparatedWeird") << commaSeparatedWeird << QString("'My") << standardFont;
|
||||
QTest::newRow("commaSeparatedBogus") << commaSeparatedBogus << bogusFont << standardFont;
|
||||
}
|
||||
|
||||
void tst_QFont::familyNameWithCommaQuote()
|
||||
{
|
||||
QFETCH(QString, familyName);
|
||||
QFETCH(QString, chosenFamilyName);
|
||||
|
||||
const int weirdFontId = QFontDatabase::addApplicationFont(":/weirdfont.otf");
|
||||
|
||||
QVERIFY(weirdFontId != -1);
|
||||
QFont f(familyName);
|
||||
QCOMPARE(f.family(), familyName);
|
||||
QCOMPARE(QFontInfo(f).family(), chosenFamilyName);
|
||||
|
||||
QFontDatabase::removeApplicationFont(weirdFontId);
|
||||
}
|
||||
|
||||
void tst_QFont::setFamilies_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("families");
|
||||
QTest::addColumn<QString>("chosenFamilyName");
|
||||
|
||||
const QString weirdFont(QLatin1String("'My, weird'' font name',"));
|
||||
const QString standardFont(QFont().defaultFamily());
|
||||
if (standardFont.isEmpty())
|
||||
QSKIP("No default font available on the system");
|
||||
|
||||
QTest::newRow("emptyFamily") << (QStringList()) << QString();
|
||||
QTest::newRow("standard") << (QStringList() << standardFont) << standardFont;
|
||||
QTest::newRow("weird") << (QStringList() << weirdFont) << weirdFont;
|
||||
QTest::newRow("standard-weird") << (QStringList() << standardFont << weirdFont) << standardFont;
|
||||
QTest::newRow("weird-standard") << (QStringList() << weirdFont << standardFont) << weirdFont;
|
||||
QTest::newRow("nonexist-weird") << (QStringList() << "NonExistentFont" << weirdFont) << weirdFont;
|
||||
}
|
||||
|
||||
void tst_QFont::setFamilies()
|
||||
{
|
||||
QFETCH(QStringList, families);
|
||||
QFETCH(QString, chosenFamilyName);
|
||||
|
||||
const int weirdFontId = QFontDatabase::addApplicationFont(":/weirdfont.otf");
|
||||
|
||||
QVERIFY(weirdFontId != -1);
|
||||
QFont f;
|
||||
f.setFamilies(families);
|
||||
if (!chosenFamilyName.isEmpty()) // Only check when it is not empty
|
||||
QCOMPARE(QFontInfo(f).family(), chosenFamilyName);
|
||||
|
||||
QFontDatabase::removeApplicationFont(weirdFontId);
|
||||
}
|
||||
|
||||
void tst_QFont::setFamiliesAndFamily_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("families");
|
||||
QTest::addColumn<QString>("family");
|
||||
QTest::addColumn<QString>("chosenFamilyName");
|
||||
|
||||
const QString weirdFont(QLatin1String("'My, weird'' font name',"));
|
||||
const QString defaultFont(QFont().defaultFamily());
|
||||
if (defaultFont.isEmpty())
|
||||
QSKIP("No default font available on the system");
|
||||
|
||||
const QString timesFont(QLatin1String("Times"));
|
||||
const QString nonExistFont(QLatin1String("NonExistentFont"));
|
||||
|
||||
QTest::newRow("emptyFamily") << (QStringList()) << QString() << QString();
|
||||
QTest::newRow("firstInFamilies") << (QStringList() << defaultFont << timesFont) << weirdFont << defaultFont;
|
||||
QTest::newRow("secondInFamilies") << (QStringList() << nonExistFont << weirdFont) << defaultFont << weirdFont;
|
||||
QTest::newRow("family") << (QStringList() << nonExistFont) << defaultFont << defaultFont;
|
||||
}
|
||||
|
||||
void tst_QFont::setFamiliesAndFamily()
|
||||
{
|
||||
QFETCH(QStringList, families);
|
||||
QFETCH(QString, family);
|
||||
QFETCH(QString, chosenFamilyName);
|
||||
|
||||
const int weirdFontId = QFontDatabase::addApplicationFont(":/weirdfont.otf");
|
||||
|
||||
QVERIFY(weirdFontId != -1);
|
||||
QFont f;
|
||||
f.setFamily(family);
|
||||
f.setFamilies(families);
|
||||
if (!family.isEmpty()) // Only check when it is not empty
|
||||
QCOMPARE(QFontInfo(f).family(), chosenFamilyName);
|
||||
|
||||
QFontDatabase::removeApplicationFont(weirdFontId);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QFont)
|
||||
#include "tst_qfont.moc"
|
BIN
tests/auto/gui/text/qfont/weirdfont.otf
Normal file
BIN
tests/auto/gui/text/qfont/weirdfont.otf
Normal file
Binary file not shown.
15
tests/auto/gui/text/qfontcache/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qfontcache/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qfontcache Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qfontcache
|
||||
SOURCES
|
||||
tst_qfontcache.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
253
tests/auto/gui/text/qfontcache/tst_qfontcache.cpp
Normal file
253
tests/auto/gui/text/qfontcache/tst_qfontcache.cpp
Normal file
@ -0,0 +1,253 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
|
||||
|
||||
#include <qfont.h>
|
||||
#include <private/qfont_p.h>
|
||||
#include <private/qfontengine_p.h>
|
||||
|
||||
class tst_QFontCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QFontCache();
|
||||
virtual ~tst_QFontCache();
|
||||
|
||||
private slots:
|
||||
void engineData_data();
|
||||
void engineData();
|
||||
void engineDataFamilies_data();
|
||||
void engineDataFamilies();
|
||||
void threadedAccess();
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
QT_BEGIN_NAMESPACE
|
||||
// qfontdatabase.cpp
|
||||
Q_AUTOTEST_EXPORT void qt_setQtEnableTestFont(bool value);
|
||||
// qfontengine.cpp
|
||||
Q_AUTOTEST_EXPORT void QFontEngine_startCollectingEngines();
|
||||
Q_AUTOTEST_EXPORT QList<QFontEngine *> QFontEngine_stopCollectingEngines();
|
||||
QT_END_NAMESPACE
|
||||
#endif
|
||||
|
||||
tst_QFontCache::tst_QFontCache()
|
||||
{
|
||||
}
|
||||
|
||||
tst_QFontCache::~tst_QFontCache()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QFontCache::engineData_data()
|
||||
{
|
||||
QTest::addColumn<QString>("family");
|
||||
QTest::addColumn<QStringList>("cacheKey");
|
||||
|
||||
QTest::newRow("unquoted-family-name") << QString("Times New Roman") << QStringList({"Times New Roman"});
|
||||
QTest::newRow("quoted-family-name") << QString("'Times New Roman'") << QStringList({"Times New Roman"});
|
||||
QTest::newRow("invalid") << QString("invalid") << QStringList({"invalid"});
|
||||
QTest::newRow("multiple") << QString("invalid, Times New Roman")
|
||||
<< QStringList({"invalid", "Times New Roman"});
|
||||
QTest::newRow("multiple spaces") << QString("invalid, Times New Roman ")
|
||||
<< QStringList({"invalid", "Times New Roman"});
|
||||
QTest::newRow("multiple spaces quotes") << QString("'invalid', Times New Roman ")
|
||||
<< QStringList({"invalid", "Times New Roman"});
|
||||
QTest::newRow("multiple2") << QString("invalid, Times New Roman , foobar, 'baz'")
|
||||
<< QStringList({"invalid", "Times New Roman", "foobar", "baz"});
|
||||
QTest::newRow("invalid spaces") << QString("invalid spaces, Times New Roman ")
|
||||
<< QStringList({"invalid spaces", "Times New Roman"});
|
||||
QTest::newRow("invalid spaces quotes") << QString("'invalid spaces', 'Times New Roman' ")
|
||||
<< QStringList({"invalid spaces", "Times New Roman"});
|
||||
}
|
||||
|
||||
void tst_QFontCache::engineData()
|
||||
{
|
||||
QFETCH(QString, family);
|
||||
QFETCH(QStringList, cacheKey);
|
||||
|
||||
QFont f(family);
|
||||
f.exactMatch(); // loads engine
|
||||
|
||||
QFontPrivate *d = QFontPrivate::get(f);
|
||||
|
||||
QFontDef req = d->request;
|
||||
// copy-pasted from QFontDatabase::load(), to engineer the cache key
|
||||
if (req.pixelSize == -1) {
|
||||
req.pixelSize = std::floor(((req.pointSize * d->dpi) / 72) * 100 + 0.5) / 100;
|
||||
req.pixelSize = qRound(req.pixelSize);
|
||||
}
|
||||
if (req.pointSize < 0)
|
||||
req.pointSize = req.pixelSize*72.0/d->dpi;
|
||||
|
||||
req.families = cacheKey;
|
||||
|
||||
QFontEngineData *engineData = QFontCache::instance()->findEngineData(req);
|
||||
|
||||
QCOMPARE(engineData, QFontPrivate::get(f)->engineData);
|
||||
}
|
||||
|
||||
void tst_QFontCache::engineDataFamilies_data()
|
||||
{
|
||||
QTest::addColumn<QStringList>("families");
|
||||
|
||||
const QStringList multiple = { QLatin1String("invalid"), QLatin1String("Times New Roman") };
|
||||
const QStringList multipleQuotes = { QLatin1String("'invalid'"), QLatin1String("Times New Roman") };
|
||||
const QStringList multiple2 = { QLatin1String("invalid"), QLatin1String("Times New Roman"),
|
||||
QLatin1String("foobar"), QLatin1String("'baz'") };
|
||||
|
||||
QTest::newRow("unquoted-family-name") << QStringList(QLatin1String("Times New Roman"));
|
||||
QTest::newRow("quoted-family-name") << QStringList(QLatin1String("Times New Roman"));
|
||||
QTest::newRow("invalid") << QStringList(QLatin1String("invalid"));
|
||||
QTest::newRow("multiple") << multiple;
|
||||
QTest::newRow("multiple spaces quotes") << multipleQuotes;
|
||||
QTest::newRow("multiple2") << multiple2;
|
||||
}
|
||||
|
||||
void tst_QFontCache::engineDataFamilies()
|
||||
{
|
||||
QFETCH(QStringList, families);
|
||||
|
||||
QFont f;
|
||||
f.setFamily(QString()); // Unset the family as taken from the QGuiApplication default
|
||||
f.setFamilies(families);
|
||||
f.exactMatch(); // loads engine
|
||||
QFontPrivate *d = QFontPrivate::get(f);
|
||||
|
||||
QFontDef req = d->request;
|
||||
// copy-pasted from QFontDatabase::load(), to engineer the cache key
|
||||
if (req.pixelSize == -1) {
|
||||
req.pixelSize = std::floor(((req.pointSize * d->dpi) / 72) * 100 + 0.5) / 100;
|
||||
req.pixelSize = qRound(req.pixelSize);
|
||||
}
|
||||
if (req.pointSize < 0)
|
||||
req.pointSize = req.pixelSize*72.0/d->dpi;
|
||||
|
||||
req.families = families;
|
||||
|
||||
QFontEngineData *engineData = QFontCache::instance()->findEngineData(req);
|
||||
|
||||
QCOMPARE(engineData, QFontPrivate::get(f)->engineData);
|
||||
}
|
||||
|
||||
void tst_QFontCache::clear()
|
||||
{
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
QFontEngine_startCollectingEngines();
|
||||
#else
|
||||
// must not crash, at very least ;)
|
||||
#endif
|
||||
|
||||
QFontEngine *fontEngine = 0;
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
{
|
||||
// we're never caching the box (and the "test") font engines
|
||||
// let's ensure we're not leaking them as well as the cached ones
|
||||
qt_setQtEnableTestFont(true);
|
||||
|
||||
QFont f;
|
||||
f.setFamily("__Qt__Box__Engine__");
|
||||
f.exactMatch(); // loads engine
|
||||
}
|
||||
#endif
|
||||
{
|
||||
QFont f;
|
||||
f.setStyleHint(QFont::Serif);
|
||||
const QString familyForHint(f.defaultFamily());
|
||||
|
||||
// it should at least return a family that is available
|
||||
QVERIFY(QFontDatabase::hasFamily(familyForHint));
|
||||
f.exactMatch(); // loads engine
|
||||
|
||||
fontEngine = QFontPrivate::get(f)->engineForScript(QChar::Script_Common);
|
||||
QVERIFY(fontEngine);
|
||||
QVERIFY(QFontCache::instance()->engineCacheCount.value(fontEngine) > 0); // ensure it is cached
|
||||
|
||||
// acquire the engine to use it somewhere else:
|
||||
// (e.g. like the we do in QFontSubset() or like QRawFont does in fromFont())
|
||||
fontEngine->ref.ref();
|
||||
|
||||
// cache the engine once again; there is a special case when the engine is cached more than once
|
||||
QFontCache::instance()->insertEngine(QFontCache::Key(QFontDef(), 0, 1), fontEngine);
|
||||
}
|
||||
|
||||
// use it:
|
||||
// e.g. fontEngine->stringToCMap(..);
|
||||
|
||||
// and whilst it is alive, don't hesitate to add/remove the app-local fonts:
|
||||
// (QFontDatabase::{add,remove}ApplicationFont() clears the cache)
|
||||
QFontCache::instance()->clear();
|
||||
|
||||
// release the acquired engine:
|
||||
if (fontEngine) {
|
||||
if (!fontEngine->ref.deref())
|
||||
delete fontEngine;
|
||||
fontEngine = 0;
|
||||
}
|
||||
|
||||
// we may even exit the application now:
|
||||
QFontCache::instance()->cleanup();
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
QList<QFontEngine *> leakedEngines = QFontEngine_stopCollectingEngines();
|
||||
for (int i = 0; i < leakedEngines.size(); ++i) qWarning() << i << leakedEngines.at(i) << leakedEngines.at(i)->ref.loadRelaxed();
|
||||
// and we are not leaking!
|
||||
QCOMPARE(leakedEngines.size(), 0);
|
||||
#endif
|
||||
}
|
||||
|
||||
struct MessageHandler
|
||||
{
|
||||
MessageHandler()
|
||||
{
|
||||
oldMessageHandler = qInstallMessageHandler(myMessageHandler);
|
||||
messages.clear();
|
||||
}
|
||||
~MessageHandler()
|
||||
{
|
||||
qInstallMessageHandler(oldMessageHandler);
|
||||
}
|
||||
|
||||
inline static bool receivedMessage = false;
|
||||
inline static QtMessageHandler oldMessageHandler = nullptr;
|
||||
inline static QStringList messages;
|
||||
static void myMessageHandler(QtMsgType type, const QMessageLogContext &context, const QString &text)
|
||||
{
|
||||
if (!text.startsWith("Populating font family aliases took")) {
|
||||
receivedMessage = true;
|
||||
messages += text;
|
||||
}
|
||||
if (oldMessageHandler)
|
||||
oldMessageHandler(type, context, text);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
void tst_QFontCache::threadedAccess()
|
||||
{
|
||||
MessageHandler messageHandler;
|
||||
auto lambda = []{
|
||||
for (const auto &family : QFontDatabase::families()) {
|
||||
QFont font(family);
|
||||
QFontMetrics fontMetrics(font);
|
||||
fontMetrics.height();
|
||||
}
|
||||
};
|
||||
auto *qThread = QThread::create(lambda);
|
||||
qThread->start();
|
||||
qThread->wait();
|
||||
|
||||
std::thread stdThread(lambda);
|
||||
stdThread.join();
|
||||
QVERIFY2(!messageHandler.receivedMessage, qPrintable(messageHandler.messages.join('\n')));
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QFontCache)
|
||||
#include "tst_qfontcache.moc"
|
4
tests/auto/gui/text/qfontdatabase/BLACKLIST
Normal file
4
tests/auto/gui/text/qfontdatabase/BLACKLIST
Normal file
@ -0,0 +1,4 @@
|
||||
[systemFixedFont]
|
||||
b2qt
|
||||
# QTBUG-100948
|
||||
qnx
|
48
tests/auto/gui/text/qfontdatabase/CMakeLists.txt
Normal file
48
tests/auto/gui/text/qfontdatabase/CMakeLists.txt
Normal file
@ -0,0 +1,48 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qfontdatabase Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "LED_REAL.TTF")
|
||||
|
||||
qt_internal_add_test(tst_qfontdatabase
|
||||
SOURCES
|
||||
tst_qfontdatabase.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set_source_files_properties("../../../shared/resources/testfont.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont.ttf"
|
||||
)
|
||||
set_source_files_properties("../../../shared/resources/testfont_condensed.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont_condensed.ttf"
|
||||
)
|
||||
set_source_files_properties("../../../shared/resources/testfont_italic.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont_italic.ttf"
|
||||
)
|
||||
set_source_files_properties("../../../shared/resources/testfont_open.otf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont_open.otf"
|
||||
)
|
||||
set(testdata_resource_files
|
||||
"../../../shared/resources/testfont.ttf"
|
||||
"../../../shared/resources/testfont_condensed.ttf"
|
||||
"../../../shared/resources/testfont_italic.ttf"
|
||||
"../../../shared/resources/testfont_open.otf"
|
||||
"LED_REAL.TTF"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qfontdatabase "testdata"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${testdata_resource_files}
|
||||
)
|
||||
|
BIN
tests/auto/gui/text/qfontdatabase/LED_REAL.TTF
Normal file
BIN
tests/auto/gui/text/qfontdatabase/LED_REAL.TTF
Normal file
Binary file not shown.
34
tests/auto/gui/text/qfontdatabase/LED_REAL_readme.txt
Normal file
34
tests/auto/gui/text/qfontdatabase/LED_REAL_readme.txt
Normal file
@ -0,0 +1,34 @@
|
||||
Font: LED Real (led_real.ttf)
|
||||
Created By: Matthew Welch
|
||||
E-Mail: daffy-duck@worldnet.att.net
|
||||
Web Address: http://home.att.net/~daffy-duck
|
||||
(PGP public key available here)
|
||||
|
||||
LED Real, like all of my fonts, is free. You can use it for most
|
||||
personal or business uses you'd like, and I ask for no money. I
|
||||
would, however, like to hear from you. If you use my fonts for
|
||||
something please send me a postcard or e-mail letting me know how
|
||||
you used it. Send me a copy if you can or let me know where I can
|
||||
find your work.
|
||||
|
||||
You may use this font for graphical or printed work, but you may not
|
||||
sell it or include it in a collection of fonts (on CD or otherwise)
|
||||
being sold. You can redistribute this font as long as you charge
|
||||
nothing to receive it. If you redistribute it include this text file
|
||||
with it as is (without modifications).
|
||||
|
||||
If you use this font for commercial purposes please credit me in
|
||||
at least some little way.
|
||||
|
||||
About the font:
|
||||
|
||||
Unlike most LED/LCD style fonts mine could be recreated with an
|
||||
actual LED. I created this font working from memories of the good
|
||||
old Speak and Spell display. Since I don't have an actual Speak
|
||||
and Spell to work from I had to just do as well as I could in its
|
||||
spirit. Be warned that some characters look just like others. The
|
||||
( and the <, for instance. Also C and [. Most of these will be
|
||||
pretty clear in context. To see all the sections of the LED "lit
|
||||
up" at once use character 127 (hold down alt and type 0127 on the
|
||||
numeric keypad). This font is, of course, monospaced.
|
||||
|
509
tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
Normal file
509
tests/auto/gui/text/qfontdatabase/tst_qfontdatabase.cpp
Normal file
@ -0,0 +1,509 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#include <qfontdatabase.h>
|
||||
#include <qfontinfo.h>
|
||||
#include <qfontmetrics.h>
|
||||
#include <qtextlayout.h>
|
||||
#include <private/qrawfont_p.h>
|
||||
#include <private/qfont_p.h>
|
||||
#include <private/qfontengine_p.h>
|
||||
#include <qpa/qplatformfontdatabase.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
Q_LOGGING_CATEGORY(lcTests, "qt.text.tests")
|
||||
|
||||
class tst_QFontDatabase : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QFontDatabase();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void styles_data();
|
||||
void styles();
|
||||
|
||||
void fixedPitch_data();
|
||||
void fixedPitch();
|
||||
void systemFixedFont();
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
void trickyFonts_data();
|
||||
void trickyFonts();
|
||||
#endif
|
||||
|
||||
void widthTwoTimes_data();
|
||||
void widthTwoTimes();
|
||||
|
||||
void addAppFont_data();
|
||||
void addAppFont();
|
||||
|
||||
void addTwoAppFontsFromFamily();
|
||||
|
||||
void aliases();
|
||||
void fallbackFonts();
|
||||
|
||||
void condensedFontWidth();
|
||||
void condensedFontWidthNoFontMerging();
|
||||
void condensedFontMatching();
|
||||
|
||||
void rasterFonts();
|
||||
void smoothFonts();
|
||||
|
||||
void registerOpenTypePreferredNamesSystem();
|
||||
void registerOpenTypePreferredNamesApplication();
|
||||
|
||||
void stretchRespected();
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
void findCourier();
|
||||
#endif
|
||||
|
||||
private:
|
||||
QString m_ledFont;
|
||||
QString m_testFont;
|
||||
QString m_testFontCondensed;
|
||||
QString m_testFontItalic;
|
||||
};
|
||||
|
||||
tst_QFontDatabase::tst_QFontDatabase()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::initTestCase()
|
||||
{
|
||||
m_ledFont = QFINDTESTDATA("LED_REAL.TTF");
|
||||
m_testFont = QFINDTESTDATA("testfont.ttf");
|
||||
m_testFontCondensed = QFINDTESTDATA("testfont_condensed.ttf");
|
||||
m_testFontItalic = QFINDTESTDATA("testfont_italic.ttf");
|
||||
QVERIFY(!m_ledFont.isEmpty());
|
||||
QVERIFY(!m_testFont.isEmpty());
|
||||
QVERIFY(!m_testFontCondensed.isEmpty());
|
||||
QVERIFY(!m_testFontItalic.isEmpty());
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::styles_data()
|
||||
{
|
||||
QTest::addColumn<QString>("font");
|
||||
|
||||
QTest::newRow( "data0" ) << QString( "Times New Roman" );
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::styles()
|
||||
{
|
||||
QFETCH( QString, font );
|
||||
|
||||
QStringList styles = QFontDatabase::styles( font );
|
||||
QStringList::Iterator it = styles.begin();
|
||||
while ( it != styles.end() ) {
|
||||
QString style = *it;
|
||||
QString trimmed = style.trimmed();
|
||||
++it;
|
||||
|
||||
QCOMPARE(style, trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::fixedPitch_data()
|
||||
{
|
||||
QTest::addColumn<QString>("font");
|
||||
QTest::addColumn<bool>("fixedPitch");
|
||||
|
||||
QTest::newRow( "Times New Roman" ) << QString( "Times New Roman" ) << false;
|
||||
QTest::newRow( "Arial" ) << QString( "Arial" ) << false;
|
||||
QTest::newRow( "Andale Mono" ) << QString( "Andale Mono" ) << true;
|
||||
QTest::newRow( "Courier" ) << QString( "Courier" ) << true;
|
||||
QTest::newRow( "Courier New" ) << QString( "Courier New" ) << true;
|
||||
#ifndef Q_OS_MAC
|
||||
QTest::newRow( "Script" ) << QString( "Script" ) << false;
|
||||
QTest::newRow( "Lucida Console" ) << QString( "Lucida Console" ) << true;
|
||||
QTest::newRow( "DejaVu Sans" ) << QString( "DejaVu Sans" ) << false;
|
||||
QTest::newRow( "DejaVu Sans Mono" ) << QString( "DejaVu Sans Mono" ) << true;
|
||||
#else
|
||||
QTest::newRow( "Menlo" ) << QString( "Menlo" ) << true;
|
||||
QTest::newRow( "Monaco" ) << QString( "Monaco" ) << true;
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::fixedPitch()
|
||||
{
|
||||
QFETCH(QString, font);
|
||||
QFETCH(bool, fixedPitch);
|
||||
|
||||
if (!QFontDatabase::families().contains(font))
|
||||
QSKIP("Font not installed");
|
||||
|
||||
QCOMPARE(QFontDatabase::isFixedPitch(font), fixedPitch);
|
||||
|
||||
QFont qfont(font);
|
||||
QFontInfo fi(qfont);
|
||||
QCOMPARE(fi.fixedPitch(), fixedPitch);
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::systemFixedFont() // QTBUG-54623
|
||||
{
|
||||
QFont font = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
QFontInfo fontInfo(font);
|
||||
bool fdbSaysFixed = QFontDatabase::isFixedPitch(fontInfo.family(), fontInfo.styleName());
|
||||
qCDebug(lcTests) << "system fixed font is" << font << "really fixed?" << fdbSaysFixed << fontInfo.fixedPitch();
|
||||
QVERIFY(fdbSaysFixed);
|
||||
QVERIFY(fontInfo.fixedPitch());
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
void tst_QFontDatabase::trickyFonts_data()
|
||||
{
|
||||
QTest::addColumn<QString>("font");
|
||||
|
||||
QTest::newRow( "Geeza Pro" ) << QString( "Geeza Pro" );
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::trickyFonts()
|
||||
{
|
||||
QFETCH(QString, font);
|
||||
|
||||
if (!QFontDatabase::families().contains(font))
|
||||
QSKIP( "Font not installed");
|
||||
|
||||
QFont qfont(font);
|
||||
QFontInfo fi(qfont);
|
||||
QCOMPARE(fi.family(), font);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QFontDatabase::widthTwoTimes_data()
|
||||
{
|
||||
QTest::addColumn<QString>("font");
|
||||
QTest::addColumn<int>("pixelSize");
|
||||
QTest::addColumn<QString>("text");
|
||||
|
||||
QTest::newRow("Arial") << QString("Arial") << 1000 << QString("Some text");
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::widthTwoTimes()
|
||||
{
|
||||
QFETCH(QString, font);
|
||||
QFETCH(int, pixelSize);
|
||||
QFETCH(QString, text);
|
||||
|
||||
QFont f;
|
||||
f.setFamily(font);
|
||||
f.setPixelSize(pixelSize);
|
||||
|
||||
QFontMetrics fm(f);
|
||||
int w1 = fm.horizontalAdvance(text, 0);
|
||||
int w2 = fm.horizontalAdvance(text, 0);
|
||||
|
||||
QCOMPARE(w1, w2);
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::addAppFont_data()
|
||||
{
|
||||
QTest::addColumn<bool>("useMemoryFont");
|
||||
QTest::newRow("font file") << false;
|
||||
QTest::newRow("memory font") << true;
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::addAppFont()
|
||||
{
|
||||
QFETCH(bool, useMemoryFont);
|
||||
QSignalSpy fontDbChangedSpy(QGuiApplication::instance(), SIGNAL(fontDatabaseChanged()));
|
||||
|
||||
const QStringList oldFamilies = QFontDatabase::families();
|
||||
QVERIFY(!oldFamilies.isEmpty());
|
||||
|
||||
fontDbChangedSpy.clear();
|
||||
|
||||
int id;
|
||||
if (useMemoryFont) {
|
||||
QFile fontfile(m_ledFont);
|
||||
fontfile.open(QIODevice::ReadOnly);
|
||||
QByteArray fontdata = fontfile.readAll();
|
||||
QVERIFY(!fontdata.isEmpty());
|
||||
id = QFontDatabase::addApplicationFontFromData(fontdata);
|
||||
} else {
|
||||
id = QFontDatabase::addApplicationFont(m_ledFont);
|
||||
}
|
||||
#if defined(Q_OS_HPUX) && defined(QT_NO_FONTCONFIG)
|
||||
// Documentation says that X11 systems that don't have fontconfig
|
||||
// don't support application fonts.
|
||||
QCOMPARE(id, -1);
|
||||
return;
|
||||
#endif
|
||||
QCOMPARE(fontDbChangedSpy.size(), 1);
|
||||
if (id == -1)
|
||||
QSKIP("Skip the test since app fonts are not supported on this system");
|
||||
|
||||
const QStringList addedFamilies = QFontDatabase::applicationFontFamilies(id);
|
||||
QVERIFY(!addedFamilies.isEmpty());
|
||||
|
||||
const QStringList newFamilies = QFontDatabase::families();
|
||||
QVERIFY(!newFamilies.isEmpty());
|
||||
QVERIFY(newFamilies.size() >= oldFamilies.size());
|
||||
|
||||
for (int i = 0; i < addedFamilies.size(); ++i) {
|
||||
QString family = addedFamilies.at(i);
|
||||
QVERIFY(newFamilies.contains(family));
|
||||
QFont qfont(family);
|
||||
QFontInfo fi(qfont);
|
||||
QCOMPARE(fi.family(), family);
|
||||
}
|
||||
|
||||
QVERIFY(QFontDatabase::removeApplicationFont(id));
|
||||
QCOMPARE(fontDbChangedSpy.size(), 2);
|
||||
|
||||
QVERIFY(QFontDatabase::families().size() <= oldFamilies.size());
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::addTwoAppFontsFromFamily()
|
||||
{
|
||||
int regularId = QFontDatabase::addApplicationFont(m_testFont);
|
||||
if (regularId == -1)
|
||||
QSKIP("Skip the test since app fonts are not supported on this system");
|
||||
|
||||
int italicId = QFontDatabase::addApplicationFont(m_testFontItalic);
|
||||
QVERIFY(italicId != -1);
|
||||
|
||||
QVERIFY(!QFontDatabase::applicationFontFamilies(regularId).isEmpty());
|
||||
QVERIFY(!QFontDatabase::applicationFontFamilies(italicId).isEmpty());
|
||||
|
||||
QString regularFontName = QFontDatabase::applicationFontFamilies(regularId).first();
|
||||
QString italicFontName = QFontDatabase::applicationFontFamilies(italicId).first();
|
||||
QCOMPARE(regularFontName, italicFontName);
|
||||
|
||||
QFont italicFont = QFontDatabase::font(italicFontName,
|
||||
QString::fromLatin1("Italic"), 14);
|
||||
QVERIFY(italicFont.italic());
|
||||
|
||||
QFontDatabase::removeApplicationFont(regularId);
|
||||
QFontDatabase::removeApplicationFont(italicId);
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::aliases()
|
||||
{
|
||||
const QStringList families = QFontDatabase::families();
|
||||
QVERIFY(!families.isEmpty());
|
||||
|
||||
QString firstFont;
|
||||
for (int i = 0; i < families.size(); ++i) {
|
||||
if (!families.at(i).contains('[')) {
|
||||
firstFont = families.at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (firstFont.isEmpty())
|
||||
QSKIP("Skipped because there are no unambiguous font families on the system.");
|
||||
|
||||
QVERIFY(QFontDatabase::hasFamily(firstFont));
|
||||
const QString alias = QStringLiteral("AliasToFirstFont") + firstFont;
|
||||
QVERIFY(!QFontDatabase::hasFamily(alias));
|
||||
QPlatformFontDatabase::registerAliasToFontFamily(firstFont, alias);
|
||||
QVERIFY(QFontDatabase::hasFamily(alias));
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::fallbackFonts()
|
||||
{
|
||||
QTextLayout layout;
|
||||
QString s;
|
||||
s.append(QChar(0x31));
|
||||
s.append(QChar(0x05D0));
|
||||
layout.setText(s);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> runs = layout.glyphRuns(0, 1);
|
||||
foreach (QGlyphRun run, runs) {
|
||||
QRawFont rawFont = run.rawFont();
|
||||
QVERIFY(rawFont.isValid());
|
||||
|
||||
QCOMPARE(run.glyphIndexes().size(), 1);
|
||||
QVERIFY(run.glyphIndexes().at(0) != 0);
|
||||
}
|
||||
}
|
||||
|
||||
static QString testString()
|
||||
{
|
||||
return QStringLiteral("foo bar");
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::stretchRespected()
|
||||
{
|
||||
int italicId = QFontDatabase::addApplicationFont(m_testFontItalic);
|
||||
QVERIFY(italicId != -1);
|
||||
|
||||
QVERIFY(!QFontDatabase::applicationFontFamilies(italicId).isEmpty());
|
||||
|
||||
QString italicFontName = QFontDatabase::applicationFontFamilies(italicId).first();
|
||||
|
||||
QFont italicFont = QFontDatabase::font(italicFontName,
|
||||
QString::fromLatin1("Italic"), 14);
|
||||
QVERIFY(italicFont.italic());
|
||||
|
||||
QFont italicStretchedFont = italicFont;
|
||||
italicStretchedFont.setStretch( 400 );
|
||||
|
||||
QVERIFY(QFontMetricsF(italicFont).horizontalAdvance(QStringLiteral("foobar")) <
|
||||
QFontMetricsF(italicStretchedFont).horizontalAdvance(QStringLiteral("foobar")));
|
||||
|
||||
QFontDatabase::removeApplicationFont(italicId);
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::condensedFontWidthNoFontMerging()
|
||||
{
|
||||
int regularFontId = QFontDatabase::addApplicationFont(m_testFont);
|
||||
int condensedFontId = QFontDatabase::addApplicationFont(m_testFontCondensed);
|
||||
|
||||
QVERIFY(!QFontDatabase::applicationFontFamilies(regularFontId).isEmpty());
|
||||
QVERIFY(!QFontDatabase::applicationFontFamilies(condensedFontId).isEmpty());
|
||||
|
||||
QString regularFontName = QFontDatabase::applicationFontFamilies(regularFontId).first();
|
||||
QString condensedFontName = QFontDatabase::applicationFontFamilies(condensedFontId).first();
|
||||
|
||||
QFont condensedFont1(condensedFontName);
|
||||
if (regularFontName == condensedFontName)
|
||||
condensedFont1.setStyleName(QStringLiteral("Condensed"));
|
||||
condensedFont1.setStyleStrategy(QFont::PreferMatch);
|
||||
|
||||
QFont condensedFont2 = condensedFont1;
|
||||
condensedFont2.setStyleStrategy(QFont::StyleStrategy(QFont::NoFontMerging | QFont::PreferMatch));
|
||||
|
||||
QCOMPARE(QFontMetricsF(condensedFont2).horizontalAdvance(QStringLiteral("foobar")),
|
||||
QFontMetricsF(condensedFont1).horizontalAdvance(QStringLiteral("foobar")));
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::condensedFontWidth()
|
||||
{
|
||||
QFontDatabase::addApplicationFont(m_testFont);
|
||||
QFontDatabase::addApplicationFont(m_testFontCondensed);
|
||||
|
||||
QVERIFY(QFontDatabase::hasFamily("QtBidiTestFont"));
|
||||
if (!QFontDatabase::hasFamily("QtBidiTestFontCondensed"))
|
||||
QSKIP("This platform doesn't support font sub-family names (QTBUG-55625)");
|
||||
|
||||
// Test we really get a condensed font, and a not renormalized one (QTBUG-48043):
|
||||
QFont testFont("QtBidiTestFont");
|
||||
QFont testFontCondensed("QtBidiTestFontCondensed");
|
||||
QFontMetrics fmTF(testFont);
|
||||
QFontMetrics fmTFC(testFontCondensed);
|
||||
QVERIFY(fmTF.horizontalAdvance(testString()) > fmTFC.horizontalAdvance(testString()));
|
||||
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::condensedFontMatching()
|
||||
{
|
||||
QFontDatabase::removeAllApplicationFonts();
|
||||
QFontDatabase::addApplicationFont(m_testFontCondensed);
|
||||
if (!QFontDatabase::hasFamily("QtBidiTestFont"))
|
||||
QSKIP("This platform doesn't support preferred font family names (QTBUG-53478)");
|
||||
QFontDatabase::addApplicationFont(m_testFont);
|
||||
|
||||
// Test we correctly get the condensed font using different font matching methods:
|
||||
QFont tfcByStretch("QtBidiTestFont");
|
||||
tfcByStretch.setStretch(QFont::Condensed);
|
||||
QFont tfcByStyleName("QtBidiTestFont");
|
||||
tfcByStyleName.setStyleName("Condensed");
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
QFont f;
|
||||
f.setStyleStrategy(QFont::NoFontMerging);
|
||||
QFontPrivate *font_d = QFontPrivate::get(f);
|
||||
if (font_d->engineForScript(QChar::Script_Common)->type() != QFontEngine::Freetype)
|
||||
QEXPECT_FAIL("","No matching of sub-family by stretch on Windows", Continue);
|
||||
#endif
|
||||
|
||||
QCOMPARE(QFontMetrics(tfcByStretch).horizontalAdvance(testString()),
|
||||
QFontMetrics(tfcByStyleName).horizontalAdvance(testString()));
|
||||
|
||||
if (!QFontDatabase::hasFamily("QtBidiTestFontCondensed"))
|
||||
QSKIP("This platform doesn't support font sub-family names (QTBUG-55625)");
|
||||
|
||||
QFont tfcBySubfamilyName("QtBidiTestFontCondensed");
|
||||
QCOMPARE(QFontMetrics(tfcByStyleName).horizontalAdvance(testString()),
|
||||
QFontMetrics(tfcBySubfamilyName).horizontalAdvance(testString()));
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::rasterFonts()
|
||||
{
|
||||
QFont font(QLatin1String("Fixedsys"), 1000);
|
||||
QFontInfo fontInfo(font);
|
||||
|
||||
if (fontInfo.family() != font.family())
|
||||
QSKIP("Fixedsys font not available.");
|
||||
|
||||
QVERIFY(!QFontDatabase::isSmoothlyScalable(font.family()));
|
||||
QVERIFY(fontInfo.pointSize() != font.pointSize());
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::smoothFonts()
|
||||
{
|
||||
QFont font(QLatin1String("Arial"), 1000);
|
||||
QFontInfo fontInfo(font);
|
||||
|
||||
if (fontInfo.family() != font.family())
|
||||
QSKIP("Arial font not available.");
|
||||
|
||||
// Smooth and bitmap scaling are mutually exclusive
|
||||
QVERIFY(QFontDatabase::isSmoothlyScalable(font.family()));
|
||||
QVERIFY(!QFontDatabase::isBitmapScalable(font.family()));
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::registerOpenTypePreferredNamesSystem()
|
||||
{
|
||||
// This font family was picked because it was the only one I had installed which showcased the
|
||||
// problem
|
||||
if (!QFontDatabase::hasFamily(QString::fromLatin1("Source Code Pro ExtraLight")))
|
||||
QSKIP("Source Code Pro ExtraLight is not installed");
|
||||
|
||||
QStringList styles = QFontDatabase::styles(QString::fromLatin1("Source Code Pro"));
|
||||
QVERIFY(styles.contains(QLatin1String("ExtraLight")));
|
||||
}
|
||||
|
||||
void tst_QFontDatabase::registerOpenTypePreferredNamesApplication()
|
||||
{
|
||||
int id = QFontDatabase::addApplicationFont(QString::fromLatin1(":/testfont_open.otf"));
|
||||
if (id == -1)
|
||||
QSKIP("Skip the test since app fonts are not supported on this system");
|
||||
|
||||
QStringList styles = QFontDatabase::styles(QString::fromLatin1("QtBidiTestFont"));
|
||||
QVERIFY(styles.contains(QLatin1String("Open")));
|
||||
|
||||
QFontDatabase::removeApplicationFont(id);
|
||||
}
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
void tst_QFontDatabase::findCourier()
|
||||
{
|
||||
QFont font = QFontDatabase::font(u"Courier"_s, u""_s, 16);
|
||||
QFontInfo info(font);
|
||||
QCOMPARE(info.family(), u"Courier New"_s);
|
||||
QCOMPARE(info.pointSize(), 16);
|
||||
|
||||
font = QFontDatabase::font("Courier", "", 64);
|
||||
info = font;
|
||||
QCOMPARE(info.family(), u"Courier New"_s);
|
||||
QCOMPARE(info.pointSize(), 64);
|
||||
|
||||
// By setting "PreferBitmap" we should get Courier itself.
|
||||
font.setStyleStrategy(QFont::PreferBitmap);
|
||||
info = font;
|
||||
QCOMPARE(info.family(), u"Courier"_s);
|
||||
// Which has an upper bound on point size
|
||||
QCOMPARE(info.pointSize(), 19);
|
||||
|
||||
font.setStyleStrategy(QFont::PreferDefault);
|
||||
info = font;
|
||||
QCOMPARE(info.family(), u"Courier New"_s);
|
||||
QCOMPARE(info.pointSize(), 64);
|
||||
}
|
||||
#endif
|
||||
|
||||
QTEST_MAIN(tst_QFontDatabase)
|
||||
#include "tst_qfontdatabase.moc"
|
32
tests/auto/gui/text/qfontmetrics/CMakeLists.txt
Normal file
32
tests/auto/gui/text/qfontmetrics/CMakeLists.txt
Normal file
@ -0,0 +1,32 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qfontmetrics Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qfontmetrics
|
||||
SOURCES
|
||||
tst_qfontmetrics.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
||||
|
||||
# Resources:
|
||||
set_source_files_properties("../../../shared/resources/testfont.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont.ttf"
|
||||
)
|
||||
set(testfont_resource_files
|
||||
"../../../shared/resources/testfont.ttf"
|
||||
"ucs4font.ttf"
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qfontmetrics "testfont"
|
||||
PREFIX
|
||||
"/fonts"
|
||||
FILES
|
||||
${testfont_resource_files}
|
||||
)
|
||||
|
392
tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp
Normal file
392
tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp
Normal file
@ -0,0 +1,392 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <qfont.h>
|
||||
#include <qfontmetrics.h>
|
||||
#include <qfontdatabase.h>
|
||||
#include <private/qfontengine_p.h>
|
||||
#include <qstringlist.h>
|
||||
#include <qlist.h>
|
||||
|
||||
class tst_QFontMetrics : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void same();
|
||||
void metrics();
|
||||
void boundingRect();
|
||||
void boundingRect2();
|
||||
void elidedText_data();
|
||||
void elidedText();
|
||||
void veryNarrowElidedText();
|
||||
void averageCharWidth();
|
||||
void bypassShaping();
|
||||
void elidedMultiLength();
|
||||
void elidedMultiLengthF();
|
||||
void inFontUcs4();
|
||||
void lineWidth();
|
||||
void mnemonicTextWidth();
|
||||
void leadingBelowLine();
|
||||
void elidedMetrics();
|
||||
void zeroWidthMetrics();
|
||||
void verticalMetrics_data();
|
||||
void verticalMetrics();
|
||||
};
|
||||
|
||||
void tst_QFontMetrics::same()
|
||||
{
|
||||
QFont font;
|
||||
font.setBold(true);
|
||||
QFontMetrics fm(font);
|
||||
const QString text = QLatin1String("Some stupid STRING");
|
||||
QCOMPARE(fm.size(0, text), fm.size(0, text)) ;
|
||||
|
||||
for (int i = 10; i <= 32; ++i) {
|
||||
font.setPixelSize(i);
|
||||
QFontMetrics fm1(font);
|
||||
QCOMPARE(fm1.size(0, text), fm1.size(0, text));
|
||||
}
|
||||
|
||||
{
|
||||
QImage image;
|
||||
QFontMetrics fm2(font, &image);
|
||||
QString text2 = QLatin1String("Foo Foo");
|
||||
QCOMPARE(fm2.size(0, text2), fm2.size(0, text2)); //used to crash
|
||||
}
|
||||
|
||||
{
|
||||
QImage image;
|
||||
QFontMetricsF fm3(font, &image);
|
||||
QString text2 = QLatin1String("Foo Foo");
|
||||
QCOMPARE(fm3.size(0, text2), fm3.size(0, text2)); //used to crash
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void tst_QFontMetrics::metrics()
|
||||
{
|
||||
QFont font;
|
||||
|
||||
// Query the QFontDatabase for a specific font, store the
|
||||
// result in family, style and size.
|
||||
QStringList families = QFontDatabase::families();
|
||||
if (families.isEmpty())
|
||||
return;
|
||||
|
||||
QStringList::ConstIterator f_it, f_end = families.end();
|
||||
for (f_it = families.begin(); f_it != f_end; ++f_it) {
|
||||
const QString &family = *f_it;
|
||||
|
||||
QStringList styles = QFontDatabase::styles(family);
|
||||
QStringList::ConstIterator s_it, s_end = styles.end();
|
||||
for (s_it = styles.begin(); s_it != s_end; ++s_it) {
|
||||
const QString &style = *s_it;
|
||||
|
||||
if (QFontDatabase::isSmoothlyScalable(family, style)) {
|
||||
// smoothly scalable font... don't need to load every pointsize
|
||||
font = QFontDatabase::font(family, style, 12);
|
||||
|
||||
QFontMetrics fontmetrics(font);
|
||||
QCOMPARE(fontmetrics.ascent() + fontmetrics.descent(),
|
||||
fontmetrics.height());
|
||||
|
||||
QCOMPARE(fontmetrics.height() + fontmetrics.leading(),
|
||||
fontmetrics.lineSpacing());
|
||||
} else {
|
||||
QList<int> sizes = QFontDatabase::pointSizes(family, style);
|
||||
QVERIFY(!sizes.isEmpty());
|
||||
QList<int>::ConstIterator z_it, z_end = sizes.end();
|
||||
for (z_it = sizes.begin(); z_it != z_end; ++z_it) {
|
||||
const int size = *z_it;
|
||||
|
||||
// Initialize the font, and check if it is an exact match
|
||||
font = QFontDatabase::font(family, style, size);
|
||||
|
||||
QFontMetrics fontmetrics(font);
|
||||
QCOMPARE(fontmetrics.ascent() + fontmetrics.descent(),
|
||||
fontmetrics.height());
|
||||
QCOMPARE(fontmetrics.height() + fontmetrics.leading(),
|
||||
fontmetrics.lineSpacing());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::boundingRect()
|
||||
{
|
||||
QFont f;
|
||||
f.setPointSize(24);
|
||||
QFontMetrics fm(f);
|
||||
QRect r = fm.boundingRect(QChar('Y'));
|
||||
QVERIFY(r.top() < 0);
|
||||
r = fm.boundingRect(QString("Y"));
|
||||
QVERIFY(r.top() < 0);
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::boundingRect2()
|
||||
{
|
||||
QFont f;
|
||||
f.setPixelSize(16);
|
||||
QFontMetricsF fm(f);
|
||||
QString str("AVAVAVA vvvvvvvvvv fffffffff file");
|
||||
QRectF br = fm.boundingRect(str);
|
||||
QRectF tbr = fm.tightBoundingRect(str);
|
||||
qreal advance = fm.horizontalAdvance(str);
|
||||
// Bounding rect plus bearings should be similar to advance
|
||||
qreal bearings = fm.leftBearing(QChar('A')) + fm.rightBearing(QChar('e'));
|
||||
QVERIFY(qAbs(br.width() + bearings - advance) < fm.averageCharWidth()/2.0);
|
||||
QVERIFY(qAbs(tbr.width() + bearings - advance) < fm.averageCharWidth()/2.0);
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::elidedText_data()
|
||||
{
|
||||
QTest::addColumn<QFont>("font");
|
||||
QTest::addColumn<QString>("text");
|
||||
|
||||
QTest::newRow("helvetica hello") << QFont("helvetica",10) << QString("hello") ;
|
||||
QTest::newRow("helvetica hello &Bye") << QFont("helvetica",10) << QString("hello&Bye") ;
|
||||
}
|
||||
|
||||
|
||||
void tst_QFontMetrics::elidedText()
|
||||
{
|
||||
QFETCH(QFont, font);
|
||||
QFETCH(QString, text);
|
||||
QFontMetrics fm(font);
|
||||
int w = fm.horizontalAdvance(text);
|
||||
QString newtext = fm.elidedText(text,Qt::ElideRight,w+1, 0);
|
||||
QCOMPARE(text,newtext); // should not elide
|
||||
newtext = fm.elidedText(text,Qt::ElideRight,w-1, 0);
|
||||
QVERIFY(text != newtext); // should elide
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::veryNarrowElidedText()
|
||||
{
|
||||
QFont f;
|
||||
QFontMetrics fm(f);
|
||||
QString text("hello");
|
||||
QCOMPARE(fm.elidedText(text, Qt::ElideRight, 0), QString());
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::averageCharWidth()
|
||||
{
|
||||
QFont f;
|
||||
QFontMetrics fm(f);
|
||||
QVERIFY(fm.averageCharWidth() != 0);
|
||||
QFontMetricsF fmf(f);
|
||||
QVERIFY(fmf.averageCharWidth() != 0);
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::bypassShaping()
|
||||
{
|
||||
QFont f;
|
||||
f.setStyleStrategy(QFont::PreferNoShaping);
|
||||
f.setKerning(false);
|
||||
|
||||
QFontMetricsF fm(f);
|
||||
QString text = " A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z";
|
||||
qreal textWidth = fm.horizontalAdvance(text);
|
||||
QVERIFY(textWidth != 0);
|
||||
qreal charsWidth = 0;
|
||||
for (int i = 0; i < text.size(); ++i)
|
||||
charsWidth += fm.horizontalAdvance(text[i]);
|
||||
QCOMPARE(textWidth, charsWidth);
|
||||
}
|
||||
|
||||
template<class FontMetrics, typename PrimitiveType> void elidedMultiLength_helper()
|
||||
{
|
||||
QString text1 = QLatin1String("Long Text 1\x9cShorter\x9csmall");
|
||||
QString text1_long = "Long Text 1";
|
||||
QString text1_short = "Shorter";
|
||||
QString text1_small = "small";
|
||||
FontMetrics fm = FontMetrics(QFont());
|
||||
PrimitiveType width_long = fm.size(0, text1_long).width();
|
||||
QCOMPARE(fm.elidedText(text1,Qt::ElideRight, 8000), text1_long);
|
||||
QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_long + 1), text1_long);
|
||||
QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_long - 1), text1_short);
|
||||
PrimitiveType width_short = fm.size(0, text1_short).width();
|
||||
QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_short + 1), text1_short);
|
||||
QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_short - 1), text1_small);
|
||||
|
||||
// Not even wide enough for "small" - should use ellipsis
|
||||
QChar ellipsisChar(0x2026);
|
||||
QString text1_el = QString::fromLatin1("s") + ellipsisChar;
|
||||
PrimitiveType width_small = fm.horizontalAdvance(text1_el);
|
||||
QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_small + 1), text1_el);
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::elidedMultiLength()
|
||||
{
|
||||
elidedMultiLength_helper<QFontMetrics, int>();
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::elidedMultiLengthF()
|
||||
{
|
||||
elidedMultiLength_helper<QFontMetricsF, qreal>();
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::inFontUcs4()
|
||||
{
|
||||
int id = QFontDatabase::addApplicationFont(":/fonts/ucs4font.ttf");
|
||||
QVERIFY(id >= 0);
|
||||
|
||||
QFont font("QtTestUcs4");
|
||||
{
|
||||
QFontMetrics fm(font);
|
||||
QVERIFY(fm.inFontUcs4(0x1D7FF));
|
||||
}
|
||||
|
||||
{
|
||||
QFontMetricsF fm(font);
|
||||
QVERIFY(fm.inFontUcs4(0x1D7FF));
|
||||
}
|
||||
|
||||
{
|
||||
QFontEngine *engine = QFontPrivate::get(font)->engineForScript(QChar::Script_Common);
|
||||
QGlyphLayout glyphs;
|
||||
glyphs.numGlyphs = 3;
|
||||
uint buf[3];
|
||||
glyphs.glyphs = buf;
|
||||
|
||||
QString string;
|
||||
{
|
||||
string.append(QChar::highSurrogate(0x1D7FF));
|
||||
string.append(QChar::lowSurrogate(0x1D7FF));
|
||||
|
||||
glyphs.numGlyphs = 3;
|
||||
glyphs.glyphs[0] = 0;
|
||||
QVERIFY(engine->stringToCMap(string.constData(), string.size(),
|
||||
&glyphs, &glyphs.numGlyphs,
|
||||
QFontEngine::GlyphIndicesOnly));
|
||||
QCOMPARE(glyphs.numGlyphs, 1);
|
||||
QCOMPARE(glyphs.glyphs[0], uint(1));
|
||||
}
|
||||
{
|
||||
string.clear();
|
||||
string.append(QChar::ObjectReplacementCharacter);
|
||||
|
||||
glyphs.numGlyphs = 3;
|
||||
glyphs.glyphs[0] = 0;
|
||||
QVERIFY(engine->stringToCMap(string.constData(), string.size(),
|
||||
&glyphs, &glyphs.numGlyphs,
|
||||
QFontEngine::GlyphIndicesOnly));
|
||||
QVERIFY(glyphs.glyphs[0] != 1);
|
||||
}
|
||||
}
|
||||
|
||||
QFontDatabase::removeApplicationFont(id);
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::lineWidth()
|
||||
{
|
||||
// QTBUG-13009, QTBUG-13011
|
||||
QFont smallFont;
|
||||
smallFont.setPointSize(8);
|
||||
smallFont.setWeight(QFont::Light);
|
||||
const QFontMetrics smallFontMetrics(smallFont);
|
||||
|
||||
QFont bigFont;
|
||||
bigFont.setPointSize(40);
|
||||
bigFont.setWeight(QFont::Black);
|
||||
const QFontMetrics bigFontMetrics(bigFont);
|
||||
|
||||
QVERIFY(smallFontMetrics.lineWidth() >= 1);
|
||||
QVERIFY(smallFontMetrics.lineWidth() < bigFontMetrics.lineWidth());
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::mnemonicTextWidth()
|
||||
{
|
||||
// QTBUG-41593
|
||||
QFont f;
|
||||
QFontMetrics fm(f);
|
||||
const QString f1 = "File";
|
||||
const QString f2 = "&File";
|
||||
|
||||
QCOMPARE(fm.size(Qt::TextShowMnemonic, f1), fm.size(Qt::TextShowMnemonic, f2));
|
||||
QCOMPARE(fm.size(Qt::TextHideMnemonic, f1), fm.size(Qt::TextHideMnemonic, f2));
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::leadingBelowLine()
|
||||
{
|
||||
QScriptLine line;
|
||||
line.leading = 10;
|
||||
line.leadingIncluded = true;
|
||||
line.ascent = 5;
|
||||
QCOMPARE(line.height(), line.ascent + line.descent + line.leading);
|
||||
QCOMPARE(line.base(), line.ascent);
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::elidedMetrics()
|
||||
{
|
||||
QString testFont = QFINDTESTDATA("fonts/testfont.ttf");
|
||||
QVERIFY(!testFont.isEmpty());
|
||||
|
||||
int id = QFontDatabase::addApplicationFont(testFont);
|
||||
QVERIFY(id >= 0);
|
||||
|
||||
QFont font(QFontDatabase::applicationFontFamilies(id).at(0));
|
||||
font.setPixelSize(12.0);
|
||||
|
||||
QFontMetricsF metrics(font);
|
||||
QString s = QStringLiteral("VeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongText");
|
||||
|
||||
QRectF boundingRect = metrics.boundingRect(s);
|
||||
|
||||
QString elided = metrics.elidedText(s, Qt::ElideRight, boundingRect.width() / 2.0);
|
||||
|
||||
QRectF elidedBoundingRect = metrics.boundingRect(elided);
|
||||
|
||||
QCOMPARE(boundingRect.height(), elidedBoundingRect.height());
|
||||
QCOMPARE(boundingRect.y(), elidedBoundingRect.y());
|
||||
|
||||
QFontDatabase::removeApplicationFont(id);
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::zeroWidthMetrics()
|
||||
{
|
||||
QString zwnj(QChar(0x200c));
|
||||
QString zwsp(QChar(0x200b));
|
||||
|
||||
QFont font;
|
||||
QFontMetricsF fm(font);
|
||||
QCOMPARE(fm.horizontalAdvance(zwnj), 0);
|
||||
QCOMPARE(fm.horizontalAdvance(zwsp), 0);
|
||||
QCOMPARE(fm.boundingRect(zwnj).width(), 0);
|
||||
QCOMPARE(fm.boundingRect(zwsp).width(), 0);
|
||||
|
||||
QString string1 = QStringLiteral("(") + zwnj + QStringLiteral(")");
|
||||
QString string2 = QStringLiteral("(") + zwnj + zwnj + QStringLiteral(")");
|
||||
QString string3 = QStringLiteral("(") + zwsp + QStringLiteral(")");
|
||||
QString string4 = QStringLiteral("(") + zwsp + zwsp + QStringLiteral(")");
|
||||
|
||||
QCOMPARE(fm.horizontalAdvance(string1), fm.horizontalAdvance(string2));
|
||||
QCOMPARE(fm.horizontalAdvance(string3), fm.horizontalAdvance(string4));
|
||||
QCOMPARE(fm.boundingRect(string1).width(), fm.boundingRect(string2).width());
|
||||
QCOMPARE(fm.boundingRect(string3).width(), fm.boundingRect(string4).width());
|
||||
QCOMPARE(fm.tightBoundingRect(string1).width(), fm.tightBoundingRect(string2).width());
|
||||
QCOMPARE(fm.tightBoundingRect(string3).width(), fm.tightBoundingRect(string4).width());
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::verticalMetrics_data()
|
||||
{
|
||||
QTest::addColumn<QFont>("font");
|
||||
QStringList families = QFontDatabase::families();
|
||||
for (const QString &family : families) {
|
||||
QFont font(family);
|
||||
QTest::newRow(family.toUtf8()) << font;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QFontMetrics::verticalMetrics()
|
||||
{
|
||||
QFETCH(QFont, font);
|
||||
QFontMetrics fm(font);
|
||||
QVERIFY(fm.ascent() != 0 || fm.descent() != 0);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QFontMetrics)
|
||||
#include "tst_qfontmetrics.moc"
|
BIN
tests/auto/gui/text/qfontmetrics/ucs4font.ttf
Normal file
BIN
tests/auto/gui/text/qfontmetrics/ucs4font.ttf
Normal file
Binary file not shown.
7
tests/auto/gui/text/qglyphrun/BLACKLIST
Normal file
7
tests/auto/gui/text/qglyphrun/BLACKLIST
Normal file
@ -0,0 +1,7 @@
|
||||
# QTBUG-68860
|
||||
[mixedScripts]
|
||||
ubuntu-18.04
|
||||
ubuntu-20.04
|
||||
ubuntu-22.04
|
||||
# QTBUG-100928
|
||||
qnx
|
25
tests/auto/gui/text/qglyphrun/CMakeLists.txt
Normal file
25
tests/auto/gui/text/qglyphrun/CMakeLists.txt
Normal file
@ -0,0 +1,25 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qglyphrun Test:
|
||||
#####################################################################
|
||||
|
||||
# Resources:
|
||||
set_source_files_properties("../../../shared/resources/test.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "test.ttf"
|
||||
)
|
||||
set(testdata_resource_files
|
||||
"../../../shared/resources/test.ttf"
|
||||
"Ligatures.otf"
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qglyphrun
|
||||
SOURCES
|
||||
tst_qglyphrun.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
TESTDATA ${testdata_resource_files}
|
||||
BUILTIN_TESTDATA
|
||||
)
|
||||
|
BIN
tests/auto/gui/text/qglyphrun/Ligatures.otf
Normal file
BIN
tests/auto/gui/text/qglyphrun/Ligatures.otf
Normal file
Binary file not shown.
977
tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp
Normal file
977
tests/auto/gui/text/qglyphrun/tst_qglyphrun.cpp
Normal file
@ -0,0 +1,977 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <qglyphrun.h>
|
||||
#include <qpainter.h>
|
||||
#include <qtextlayout.h>
|
||||
#include <qfontdatabase.h>
|
||||
|
||||
// #define DEBUG_SAVE_IMAGE
|
||||
|
||||
class tst_QGlyphRun: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
#if !defined(QT_NO_RAWFONT)
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void init();
|
||||
void cleanupTestCase();
|
||||
|
||||
void constructionAndDestruction();
|
||||
void copyConstructor();
|
||||
void assignment();
|
||||
void equalsOperator_data();
|
||||
void equalsOperator();
|
||||
void isEmpty();
|
||||
void textLayoutGlyphIndexes();
|
||||
void drawExistingGlyphs();
|
||||
void drawNonExistentGlyphs();
|
||||
void drawMultiScriptText1();
|
||||
void drawMultiScriptText2();
|
||||
void drawRightToLeft();
|
||||
void detach();
|
||||
void setRawData();
|
||||
void setRawDataAndGetAsVector();
|
||||
void boundingRect();
|
||||
void mixedScripts();
|
||||
void multiLineBoundingRect();
|
||||
void defaultIgnorables();
|
||||
void stringIndexes();
|
||||
void retrievalFlags_data();
|
||||
void retrievalFlags();
|
||||
void objectReplacementCharacter();
|
||||
|
||||
private:
|
||||
int m_testFontId;
|
||||
QFont m_testFont;
|
||||
bool m_testFont_ok;
|
||||
#endif // QT_NO_RAWFONT
|
||||
|
||||
};
|
||||
|
||||
#if !defined(QT_NO_RAWFONT)
|
||||
|
||||
Q_DECLARE_METATYPE(QGlyphRun);
|
||||
|
||||
void tst_QGlyphRun::initTestCase()
|
||||
{
|
||||
m_testFont_ok = false;
|
||||
|
||||
m_testFontId = QFontDatabase::addApplicationFont(QFINDTESTDATA("test.ttf"));
|
||||
QVERIFY(m_testFontId >= 0);
|
||||
|
||||
m_testFont = QFont("QtsSpecialTestFont");
|
||||
|
||||
QCOMPARE(QFontInfo(m_testFont).family(), QString::fromLatin1("QtsSpecialTestFont"));
|
||||
|
||||
m_testFont_ok = true;
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::init()
|
||||
{
|
||||
if (!m_testFont_ok)
|
||||
QSKIP("Test font is not working correctly");
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::cleanupTestCase()
|
||||
{
|
||||
QFontDatabase::removeApplicationFont(m_testFontId);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::constructionAndDestruction()
|
||||
{
|
||||
QGlyphRun glyphIndexes;
|
||||
}
|
||||
|
||||
static QGlyphRun make_dummy_indexes()
|
||||
{
|
||||
QGlyphRun glyphs;
|
||||
|
||||
QList<quint32> glyphIndexes;
|
||||
QList<QPointF> positions;
|
||||
QFont font;
|
||||
font.setPointSize(18);
|
||||
|
||||
glyphIndexes.append(1);
|
||||
glyphIndexes.append(2);
|
||||
glyphIndexes.append(3);
|
||||
|
||||
positions.append(QPointF(1, 2));
|
||||
positions.append(QPointF(3, 4));
|
||||
positions.append(QPointF(5, 6));
|
||||
|
||||
glyphs.setRawFont(QRawFont::fromFont(font));
|
||||
glyphs.setGlyphIndexes(glyphIndexes);
|
||||
glyphs.setPositions(positions);
|
||||
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::copyConstructor()
|
||||
{
|
||||
QGlyphRun glyphs;
|
||||
|
||||
{
|
||||
QList<quint32> glyphIndexes;
|
||||
QList<QPointF> positions;
|
||||
QFont font;
|
||||
font.setPointSize(18);
|
||||
|
||||
glyphIndexes.append(1);
|
||||
glyphIndexes.append(2);
|
||||
glyphIndexes.append(3);
|
||||
|
||||
positions.append(QPointF(1, 2));
|
||||
positions.append(QPointF(3, 4));
|
||||
positions.append(QPointF(5, 6));
|
||||
|
||||
glyphs.setRawFont(QRawFont::fromFont(font));
|
||||
glyphs.setGlyphIndexes(glyphIndexes);
|
||||
glyphs.setPositions(positions);
|
||||
}
|
||||
|
||||
QGlyphRun otherGlyphs(glyphs);
|
||||
QCOMPARE(otherGlyphs.rawFont(), glyphs.rawFont());
|
||||
QCOMPARE(glyphs.glyphIndexes(), otherGlyphs.glyphIndexes());
|
||||
QCOMPARE(glyphs.positions(), otherGlyphs.positions());
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::assignment()
|
||||
{
|
||||
QGlyphRun glyphs(make_dummy_indexes());
|
||||
|
||||
QGlyphRun otherGlyphs = glyphs;
|
||||
QCOMPARE(otherGlyphs.rawFont(), glyphs.rawFont());
|
||||
QCOMPARE(glyphs.glyphIndexes(), otherGlyphs.glyphIndexes());
|
||||
QCOMPARE(glyphs.positions(), otherGlyphs.positions());
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::equalsOperator_data()
|
||||
{
|
||||
QTest::addColumn<QGlyphRun>("one");
|
||||
QTest::addColumn<QGlyphRun>("two");
|
||||
QTest::addColumn<bool>("equals");
|
||||
|
||||
QGlyphRun one(make_dummy_indexes());
|
||||
QGlyphRun two(make_dummy_indexes());
|
||||
|
||||
QTest::newRow("Identical") << one << two << true;
|
||||
|
||||
{
|
||||
QGlyphRun busted(two);
|
||||
|
||||
QList<QPointF> positions = busted.positions();
|
||||
positions[2] += QPointF(1, 1);
|
||||
busted.setPositions(positions);
|
||||
|
||||
|
||||
QTest::newRow("Different positions") << one << busted << false;
|
||||
}
|
||||
|
||||
{
|
||||
QGlyphRun busted(two);
|
||||
|
||||
QFont font;
|
||||
font.setPixelSize(busted.rawFont().pixelSize() * 2);
|
||||
busted.setRawFont(QRawFont::fromFont(font));
|
||||
|
||||
QTest::newRow("Different fonts") << one << busted << false;
|
||||
}
|
||||
|
||||
{
|
||||
QGlyphRun busted(two);
|
||||
|
||||
QList<quint32> glyphIndexes = busted.glyphIndexes();
|
||||
glyphIndexes[2] += 1;
|
||||
busted.setGlyphIndexes(glyphIndexes);
|
||||
|
||||
QTest::newRow("Different glyph indexes") << one << busted << false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::equalsOperator()
|
||||
{
|
||||
QFETCH(QGlyphRun, one);
|
||||
QFETCH(QGlyphRun, two);
|
||||
QFETCH(bool, equals);
|
||||
|
||||
QCOMPARE(one == two, equals);
|
||||
QCOMPARE(one != two, !equals);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::isEmpty()
|
||||
{
|
||||
QGlyphRun glyphs;
|
||||
QVERIFY(glyphs.isEmpty());
|
||||
|
||||
glyphs.setGlyphIndexes(QList<quint32>() << 1 << 2 << 3);
|
||||
QVERIFY(!glyphs.isEmpty());
|
||||
|
||||
glyphs.clear();
|
||||
QVERIFY(glyphs.isEmpty());
|
||||
|
||||
QList<quint32> glyphIndexes = QList<quint32>() << 1 << 2 << 3;
|
||||
QList<QPointF> positions = QList<QPointF>() << QPointF(0, 0) << QPointF(0, 0) << QPointF(0, 0);
|
||||
glyphs.setRawData(glyphIndexes.constData(), positions.constData(), glyphIndexes.size());
|
||||
QVERIFY(!glyphs.isEmpty());
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::textLayoutGlyphIndexes()
|
||||
{
|
||||
QString s;
|
||||
s.append(QLatin1Char('A'));
|
||||
s.append(QChar(0xe000));
|
||||
|
||||
QTextLayout layout(s);
|
||||
layout.setFont(m_testFont);
|
||||
layout.setCacheEnabled(true);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> listOfGlyphs = layout.glyphRuns();
|
||||
|
||||
QCOMPARE(listOfGlyphs.size(), 1);
|
||||
|
||||
QGlyphRun glyphs = listOfGlyphs.at(0);
|
||||
|
||||
QCOMPARE(glyphs.glyphIndexes().size(), 2);
|
||||
QCOMPARE(glyphs.glyphIndexes().at(0), quint32(2));
|
||||
QCOMPARE(glyphs.glyphIndexes().at(1), quint32(1));
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::drawExistingGlyphs()
|
||||
{
|
||||
QPixmap textLayoutDraw(1000, 1000);
|
||||
QPixmap drawGlyphs(1000, 1000);
|
||||
|
||||
textLayoutDraw.fill(Qt::white);
|
||||
drawGlyphs.fill(Qt::white);
|
||||
|
||||
QString s;
|
||||
s.append(QLatin1Char('A'));
|
||||
s.append(QChar(0xe000));
|
||||
|
||||
QTextLayout layout(s);
|
||||
layout.setFont(m_testFont);
|
||||
layout.setCacheEnabled(true);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
{
|
||||
QPainter p(&textLayoutDraw);
|
||||
layout.draw(&p, QPointF(50, 50));
|
||||
}
|
||||
|
||||
QGlyphRun glyphs = layout.glyphRuns().size() > 0
|
||||
? layout.glyphRuns().at(0)
|
||||
: QGlyphRun();
|
||||
|
||||
{
|
||||
QPainter p(&drawGlyphs);
|
||||
p.drawGlyphRun(QPointF(50, 50), glyphs);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
textLayoutDraw.save("drawExistingGlyphs_textLayoutDraw.png");
|
||||
drawGlyphs.save("drawExistingGlyphs_drawGlyphIndexes.png");
|
||||
#endif
|
||||
|
||||
QCOMPARE(textLayoutDraw, drawGlyphs);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::setRawData()
|
||||
{
|
||||
QGlyphRun glyphRun;
|
||||
glyphRun.setRawFont(QRawFont::fromFont(m_testFont));
|
||||
glyphRun.setGlyphIndexes(QList<quint32>() << 2 << 2 << 2);
|
||||
glyphRun.setPositions(QList<QPointF>() << QPointF(2, 3) << QPointF(20, 3) << QPointF(10, 20));
|
||||
|
||||
QPixmap baseline(100, 50);
|
||||
baseline.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&baseline);
|
||||
p.drawGlyphRun(QPointF(3, 2), glyphRun);
|
||||
}
|
||||
|
||||
QGlyphRun baselineCopied = glyphRun;
|
||||
|
||||
quint32 glyphIndexArray[3] = { 2, 2, 2 };
|
||||
QPointF glyphPositionArray[3] = { QPointF(2, 3), QPointF(20, 3), QPointF(10, 20) };
|
||||
|
||||
glyphRun.setRawData(glyphIndexArray, glyphPositionArray, 3);
|
||||
|
||||
QPixmap rawDataGlyphs(100, 50);
|
||||
rawDataGlyphs.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&rawDataGlyphs);
|
||||
p.drawGlyphRun(QPointF(3, 2), glyphRun);
|
||||
}
|
||||
|
||||
quint32 otherGlyphIndexArray[1] = { 2 };
|
||||
QPointF otherGlyphPositionArray[1] = { QPointF(2, 3) };
|
||||
|
||||
glyphRun.setRawData(otherGlyphIndexArray, otherGlyphPositionArray, 1);
|
||||
|
||||
QPixmap baselineCopiedPixmap(100, 50);
|
||||
baselineCopiedPixmap.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&baselineCopiedPixmap);
|
||||
p.drawGlyphRun(QPointF(3, 2), baselineCopied);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
baseline.save("setRawData_baseline.png");
|
||||
rawDataGlyphs.save("setRawData_rawDataGlyphs.png");
|
||||
baselineCopiedPixmap.save("setRawData_baselineCopiedPixmap.png");
|
||||
#endif
|
||||
|
||||
QCOMPARE(rawDataGlyphs, baseline);
|
||||
QCOMPARE(baselineCopiedPixmap, baseline);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::setRawDataAndGetAsVector()
|
||||
{
|
||||
QList<quint32> glyphIndexArray;
|
||||
glyphIndexArray << 3 << 2 << 1 << 4;
|
||||
|
||||
QList<QPointF> glyphPositionArray;
|
||||
glyphPositionArray << QPointF(1, 2) << QPointF(3, 4) << QPointF(5, 6) << QPointF(7, 8);
|
||||
|
||||
QGlyphRun glyphRun;
|
||||
glyphRun.setRawData(glyphIndexArray.constData(), glyphPositionArray.constData(), 4);
|
||||
|
||||
QList<quint32> glyphIndexes = glyphRun.glyphIndexes();
|
||||
QList<QPointF> glyphPositions = glyphRun.positions();
|
||||
|
||||
QCOMPARE(glyphIndexes.size(), 4);
|
||||
QCOMPARE(glyphPositions.size(), 4);
|
||||
|
||||
QCOMPARE(glyphIndexes, glyphIndexArray);
|
||||
QCOMPARE(glyphPositions, glyphPositionArray);
|
||||
|
||||
QGlyphRun otherGlyphRun;
|
||||
otherGlyphRun.setGlyphIndexes(glyphIndexArray);
|
||||
otherGlyphRun.setPositions(glyphPositionArray);
|
||||
|
||||
QCOMPARE(glyphRun, otherGlyphRun);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::drawNonExistentGlyphs()
|
||||
{
|
||||
QList<quint32> glyphIndexes;
|
||||
glyphIndexes.append(4);
|
||||
|
||||
QList<QPointF> glyphPositions;
|
||||
glyphPositions.append(QPointF(0, 0));
|
||||
|
||||
QGlyphRun glyphs;
|
||||
glyphs.setGlyphIndexes(glyphIndexes);
|
||||
glyphs.setPositions(glyphPositions);
|
||||
glyphs.setRawFont(QRawFont::fromFont(m_testFont));
|
||||
|
||||
QPixmap image(1000, 1000);
|
||||
image.fill(Qt::white);
|
||||
|
||||
QPixmap imageBefore = image;
|
||||
{
|
||||
QPainter p(&image);
|
||||
p.drawGlyphRun(QPointF(50, 50), glyphs);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
image.save("drawNonExistentGlyphs.png");
|
||||
#endif
|
||||
|
||||
QCOMPARE(image, imageBefore); // Should be unchanged
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::drawMultiScriptText1()
|
||||
{
|
||||
QString text;
|
||||
text += QChar(0x03D0); // Greek, beta
|
||||
|
||||
QTextLayout textLayout(text);
|
||||
textLayout.setCacheEnabled(true);
|
||||
textLayout.beginLayout();
|
||||
textLayout.createLine();
|
||||
textLayout.endLayout();
|
||||
|
||||
QPixmap textLayoutDraw(1000, 1000);
|
||||
textLayoutDraw.fill(Qt::white);
|
||||
|
||||
QPixmap drawGlyphs(1000, 1000);
|
||||
drawGlyphs.fill(Qt::white);
|
||||
|
||||
QList<QGlyphRun> glyphsList = textLayout.glyphRuns();
|
||||
QCOMPARE(glyphsList.size(), 1);
|
||||
|
||||
{
|
||||
QPainter p(&textLayoutDraw);
|
||||
textLayout.draw(&p, QPointF(50, 50));
|
||||
}
|
||||
|
||||
{
|
||||
QPainter p(&drawGlyphs);
|
||||
foreach (QGlyphRun glyphs, glyphsList)
|
||||
p.drawGlyphRun(QPointF(50, 50), glyphs);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
textLayoutDraw.save("drawMultiScriptText1_textLayoutDraw.png");
|
||||
drawGlyphs.save("drawMultiScriptText1_drawGlyphIndexes.png");
|
||||
#endif
|
||||
|
||||
QCOMPARE(drawGlyphs, textLayoutDraw);
|
||||
}
|
||||
|
||||
|
||||
void tst_QGlyphRun::drawMultiScriptText2()
|
||||
{
|
||||
QString text;
|
||||
text += QChar(0x0621); // Arabic, Hamza
|
||||
text += QChar(0x03D0); // Greek, beta
|
||||
|
||||
QTextLayout textLayout(text);
|
||||
textLayout.setCacheEnabled(true);
|
||||
textLayout.beginLayout();
|
||||
textLayout.createLine();
|
||||
textLayout.endLayout();
|
||||
|
||||
QPixmap textLayoutDraw(1000, 1000);
|
||||
textLayoutDraw.fill(Qt::white);
|
||||
|
||||
QPixmap drawGlyphs(1000, 1000);
|
||||
drawGlyphs.fill(Qt::white);
|
||||
|
||||
QList<QGlyphRun> glyphsList = textLayout.glyphRuns();
|
||||
QCOMPARE(glyphsList.size(), 2);
|
||||
|
||||
{
|
||||
QPainter p(&textLayoutDraw);
|
||||
textLayout.draw(&p, QPointF(50, 50));
|
||||
}
|
||||
|
||||
{
|
||||
QPainter p(&drawGlyphs);
|
||||
foreach (QGlyphRun glyphs, glyphsList)
|
||||
p.drawGlyphRun(QPointF(50, 50), glyphs);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
textLayoutDraw.save("drawMultiScriptText2_textLayoutDraw.png");
|
||||
drawGlyphs.save("drawMultiScriptText2_drawGlyphIndexes.png");
|
||||
#endif
|
||||
|
||||
QCOMPARE(drawGlyphs, textLayoutDraw);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::detach()
|
||||
{
|
||||
|
||||
#define ARG(...) __VA_ARGS__
|
||||
|
||||
QGlyphRun glyphs;
|
||||
|
||||
glyphs.setGlyphIndexes(QList<quint32>() << 1 << 2 << 3);
|
||||
|
||||
QGlyphRun otherGlyphs;
|
||||
otherGlyphs = glyphs;
|
||||
|
||||
QCOMPARE(otherGlyphs.glyphIndexes(), glyphs.glyphIndexes());
|
||||
|
||||
otherGlyphs.setGlyphIndexes(QList<quint32>() << 4 << 5 << 6);
|
||||
|
||||
QCOMPARE(otherGlyphs.glyphIndexes(), ARG({4, 5, 6}));
|
||||
QCOMPARE(glyphs.glyphIndexes(), ARG({1, 2, 3}));
|
||||
|
||||
#undef ARG
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::drawRightToLeft()
|
||||
{
|
||||
QString s;
|
||||
s.append(QChar(1575));
|
||||
s.append(QChar(1578));
|
||||
|
||||
QPixmap textLayoutDraw(1000, 1000);
|
||||
QPixmap drawGlyphs(1000, 1000);
|
||||
|
||||
textLayoutDraw.fill(Qt::white);
|
||||
drawGlyphs.fill(Qt::white);
|
||||
|
||||
QTextLayout layout(s);
|
||||
layout.setCacheEnabled(true);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
{
|
||||
QPainter p(&textLayoutDraw);
|
||||
layout.draw(&p, QPointF(50, 50));
|
||||
}
|
||||
|
||||
QGlyphRun glyphs = layout.glyphRuns().size() > 0
|
||||
? layout.glyphRuns().at(0)
|
||||
: QGlyphRun();
|
||||
|
||||
{
|
||||
QPainter p(&drawGlyphs);
|
||||
p.drawGlyphRun(QPointF(50, 50), glyphs);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
textLayoutDraw.save("drawRightToLeft_textLayoutDraw.png");
|
||||
drawGlyphs.save("drawRightToLeft_drawGlyphIndexes.png");
|
||||
#endif
|
||||
|
||||
QCOMPARE(textLayoutDraw, drawGlyphs);
|
||||
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::boundingRect()
|
||||
{
|
||||
QString s(QLatin1String("AbCdE"));
|
||||
|
||||
QRawFont rawFont(QRawFont::fromFont(QFont()));
|
||||
QVERIFY(rawFont.isValid());
|
||||
QList<quint32> glyphIndexes = rawFont.glyphIndexesForString(s);
|
||||
QList<QPointF> positions = rawFont.advancesForGlyphIndexes(glyphIndexes);
|
||||
QCOMPARE(glyphIndexes.size(), s.size());
|
||||
QCOMPARE(positions.size(), glyphIndexes.size());
|
||||
|
||||
QGlyphRun glyphs;
|
||||
glyphs.setRawFont(rawFont);
|
||||
glyphs.setGlyphIndexes(glyphIndexes);
|
||||
glyphs.setPositions(positions);
|
||||
|
||||
QRectF boundingRect = glyphs.boundingRect();
|
||||
|
||||
glyphs.clear();
|
||||
glyphs.setRawFont(rawFont);
|
||||
glyphs.setRawData(glyphIndexes.constData(), positions.constData(), glyphIndexes.size());
|
||||
QCOMPARE(glyphs.boundingRect(), boundingRect);
|
||||
|
||||
boundingRect = QRectF(0, 0, 1, 1);
|
||||
glyphs.setBoundingRect(boundingRect);
|
||||
QCOMPARE(glyphs.boundingRect(), boundingRect);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::mixedScripts()
|
||||
{
|
||||
QString s;
|
||||
s += QChar(0x31); // The character '1'
|
||||
s += QChar(0xbc14); // Hangul character
|
||||
|
||||
QTextLayout layout;
|
||||
layout.setFont(m_testFont);
|
||||
layout.setText(s);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||||
QCOMPARE(glyphRuns.size(), 2);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::multiLineBoundingRect()
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText("Foo Bar");
|
||||
layout.beginLayout();
|
||||
|
||||
QTextLine line = layout.createLine();
|
||||
line.setNumColumns(4);
|
||||
line.setPosition(QPointF(0, 0));
|
||||
|
||||
line = layout.createLine();
|
||||
line.setPosition(QPointF(0, 10));
|
||||
|
||||
layout.endLayout();
|
||||
|
||||
QCOMPARE(layout.lineCount(), 2);
|
||||
|
||||
QList<QGlyphRun> firstLineGlyphRuns = layout.lineAt(0).glyphRuns();
|
||||
QList<QGlyphRun> allGlyphRuns = layout.glyphRuns();
|
||||
QCOMPARE(firstLineGlyphRuns.size(), 1);
|
||||
QCOMPARE(allGlyphRuns.size(), 1);
|
||||
|
||||
QGlyphRun firstLineGlyphRun = firstLineGlyphRuns.first();
|
||||
QGlyphRun allGlyphRun = allGlyphRuns.first();
|
||||
|
||||
QVERIFY(firstLineGlyphRun.boundingRect().height() < allGlyphRun.boundingRect().height());
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::defaultIgnorables()
|
||||
{
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setFont(QFont("QtsSpecialTestFont"));
|
||||
layout.setText(QChar(0x200D));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> runs = layout.glyphRuns();
|
||||
QCOMPARE(runs.size(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setFont(QFont("QtsSpecialTestFont"));
|
||||
layout.setText(QStringLiteral("AAA") + QChar(0xFE0F) + QStringLiteral("111"));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> runs = layout.glyphRuns();
|
||||
QVERIFY(!runs.isEmpty());
|
||||
|
||||
bool hasFullMainFontRun = false;
|
||||
for (const QGlyphRun &run : runs) {
|
||||
// QtsSpecialFont will be used for at least five characters: AA[...]111
|
||||
// Depending on the font selected for the 0xFE0F variant selector, the
|
||||
// third 'A' may be in QtsSpecialFont or in the fallback. This is platform-specific,
|
||||
// so we accept either.
|
||||
if (run.rawFont().familyName() == QStringLiteral("QtsSpecialTestFont")
|
||||
&& run.glyphIndexes().size() >= 5) {
|
||||
hasFullMainFontRun = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
QVERIFY(hasFullMainFontRun);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::stringIndexes()
|
||||
{
|
||||
int ligatureFontId = QFontDatabase::addApplicationFont(QFINDTESTDATA("Ligatures.otf"));
|
||||
QVERIFY(ligatureFontId >= 0);
|
||||
|
||||
QFont ligatureFont = QFont("QtLigatures");
|
||||
QCOMPARE(QFontInfo(ligatureFont).family(), QString::fromLatin1("QtLigatures"));
|
||||
|
||||
QTextLayout::GlyphRunRetrievalFlags retrievalFlags
|
||||
= QTextLayout::RetrieveGlyphIndexes | QTextLayout::RetrieveStringIndexes;
|
||||
|
||||
// Three characters -> three glyphs
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText("f i");
|
||||
layout.setFont(ligatureFont);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(-1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 3);
|
||||
QCOMPARE(stringIndexes.at(0), 0);
|
||||
QCOMPARE(stringIndexes.at(1), 1);
|
||||
QCOMPARE(stringIndexes.at(2), 2);
|
||||
}
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(2, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 1);
|
||||
QCOMPARE(stringIndexes.at(0), 2);
|
||||
}
|
||||
}
|
||||
|
||||
// Two characters -> one glyph
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText("fi");
|
||||
layout.setFont(ligatureFont);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(-1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 1);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(233));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 1);
|
||||
QCOMPARE(stringIndexes.at(0), 0);
|
||||
}
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 1);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(233));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 1);
|
||||
QCOMPARE(stringIndexes.at(0), 1);
|
||||
}
|
||||
}
|
||||
|
||||
// Four characters -> three glyphs
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText("ffii");
|
||||
layout.setFont(ligatureFont);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(-1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 3);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(71));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(1), uint(233));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(2), uint(74));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 3);
|
||||
QCOMPARE(stringIndexes.at(0), uint(0));
|
||||
QCOMPARE(stringIndexes.at(1), uint(1));
|
||||
QCOMPARE(stringIndexes.at(2), uint(3));
|
||||
}
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(1, 1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 1);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(233));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 1);
|
||||
QCOMPARE(stringIndexes.at(0), uint(1));
|
||||
}
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(1, 2, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 1);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(233));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 1);
|
||||
QCOMPARE(stringIndexes.at(0), uint(1));
|
||||
}
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(1, 3, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 2);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(233));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(1), uint(74));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 2);
|
||||
QCOMPARE(stringIndexes.at(0), 1);
|
||||
QCOMPARE(stringIndexes.at(1), 3);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// One character -> two glyphs
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(QChar(0xe6)); // LATIN SMALL LETTER AE
|
||||
layout.setFont(ligatureFont);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(-1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 2);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(66));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(1), uint(70));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 2);
|
||||
QCOMPARE(stringIndexes.at(0), uint(0));
|
||||
QCOMPARE(stringIndexes.at(1), uint(0));
|
||||
}
|
||||
|
||||
// Three characters -> four glyphs
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(QString('f') + QChar(0xe6) + QChar('i'));
|
||||
layout.setFont(ligatureFont);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(-1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 4);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(71));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(1), uint(66));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(2), uint(70));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(3), uint(74));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 4);
|
||||
QCOMPARE(stringIndexes.at(0), 0);
|
||||
QCOMPARE(stringIndexes.at(1), 1);
|
||||
QCOMPARE(stringIndexes.at(2), 1);
|
||||
QCOMPARE(stringIndexes.at(3), 2);
|
||||
}
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 3);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(66));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(1), uint(70));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(2), uint(74));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 3);
|
||||
QCOMPARE(stringIndexes.at(0), 1);
|
||||
QCOMPARE(stringIndexes.at(1), 1);
|
||||
QCOMPARE(stringIndexes.at(2), 2);
|
||||
}
|
||||
|
||||
{
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(0, 2, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 3);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(71));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(1), uint(66));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(2), uint(70));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 3);
|
||||
QCOMPARE(stringIndexes.at(0), 0);
|
||||
QCOMPARE(stringIndexes.at(1), 1);
|
||||
QCOMPARE(stringIndexes.at(2), 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Five characters -> five glyphs
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setText(QLatin1String("ffi") + QChar(0xe6) + QLatin1Char('i'));
|
||||
layout.setFont(ligatureFont);
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(-1, -1, retrievalFlags);
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().size(), 5);
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(0), uint(71));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(1), uint(233));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(2), uint(66));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(3), uint(70));
|
||||
QCOMPARE(glyphRuns.at(0).glyphIndexes().at(4), uint(74));
|
||||
|
||||
QList<qsizetype> stringIndexes = glyphRuns.at(0).stringIndexes();
|
||||
QCOMPARE(stringIndexes.size(), 5);
|
||||
QCOMPARE(stringIndexes.at(0), 0);
|
||||
QCOMPARE(stringIndexes.at(1), 1);
|
||||
QCOMPARE(stringIndexes.at(2), 3);
|
||||
QCOMPARE(stringIndexes.at(3), 3);
|
||||
QCOMPARE(stringIndexes.at(4), 4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::retrievalFlags_data()
|
||||
{
|
||||
QTest::addColumn<QTextLayout::GlyphRunRetrievalFlags>("flags");
|
||||
QTest::addColumn<bool>("expectedGlyphIndexes");
|
||||
QTest::addColumn<bool>("expectedStringIndexes");
|
||||
QTest::addColumn<bool>("expectedString");
|
||||
QTest::addColumn<bool>("expectedGlyphPositions");
|
||||
|
||||
QTest::newRow("Glyph indexes")
|
||||
<< QTextLayout::GlyphRunRetrievalFlags(QTextLayout::RetrieveGlyphIndexes)
|
||||
<< true << false << false << false;
|
||||
QTest::newRow("Glyph Positions")
|
||||
<< QTextLayout::GlyphRunRetrievalFlags(QTextLayout::RetrieveGlyphPositions)
|
||||
<< false << false << false << true;
|
||||
QTest::newRow("String indexes")
|
||||
<< QTextLayout::GlyphRunRetrievalFlags(QTextLayout::RetrieveStringIndexes)
|
||||
<< false << true << false << false;
|
||||
QTest::newRow("String")
|
||||
<< QTextLayout::GlyphRunRetrievalFlags(QTextLayout::RetrieveString)
|
||||
<< false << false << true << false;
|
||||
|
||||
QTest::newRow("Default")
|
||||
<< QTextLayout::GlyphRunRetrievalFlags(QTextLayout::DefaultRetrievalFlags)
|
||||
<< true << false << false << true;
|
||||
QTest::newRow("All")
|
||||
<< QTextLayout::GlyphRunRetrievalFlags(QTextLayout::RetrieveAll)
|
||||
<< true << true << true << true;
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::retrievalFlags()
|
||||
{
|
||||
QFETCH(QTextLayout::GlyphRunRetrievalFlags, flags);
|
||||
QFETCH(bool, expectedGlyphIndexes);
|
||||
QFETCH(bool, expectedStringIndexes);
|
||||
QFETCH(bool, expectedString);
|
||||
QFETCH(bool, expectedGlyphPositions);
|
||||
|
||||
QTextLayout layout;
|
||||
layout.setText(QLatin1String("abc"));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns(-1, -1, flags);
|
||||
QVERIFY(!glyphRuns.isEmpty());
|
||||
|
||||
QGlyphRun firstGlyphRun = glyphRuns.first();
|
||||
QCOMPARE(firstGlyphRun.glyphIndexes().isEmpty(), !expectedGlyphIndexes);
|
||||
QCOMPARE(firstGlyphRun.stringIndexes().isEmpty(), !expectedStringIndexes);
|
||||
QCOMPARE(firstGlyphRun.sourceString().isEmpty(), !expectedString);
|
||||
QCOMPARE(firstGlyphRun.positions().isEmpty(), !expectedGlyphPositions);
|
||||
}
|
||||
|
||||
void tst_QGlyphRun::objectReplacementCharacter()
|
||||
{
|
||||
QTextLayout layout;
|
||||
layout.setFont(m_testFont);
|
||||
layout.setText(QStringLiteral("\uFFFC"));
|
||||
layout.beginLayout();
|
||||
layout.createLine();
|
||||
layout.endLayout();
|
||||
|
||||
QList<QGlyphRun> glyphRuns = layout.glyphRuns();
|
||||
QCOMPARE(glyphRuns.size(), 1);
|
||||
QCOMPARE(glyphRuns.first().glyphIndexes().size(), 1);
|
||||
QCOMPARE(glyphRuns.first().glyphIndexes().first(), 5);
|
||||
}
|
||||
|
||||
#endif // QT_NO_RAWFONT
|
||||
|
||||
QTEST_MAIN(tst_QGlyphRun)
|
||||
#include "tst_qglyphrun.moc"
|
||||
|
14
tests/auto/gui/text/qinputcontrol/CMakeLists.txt
Normal file
14
tests/auto/gui/text/qinputcontrol/CMakeLists.txt
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qinputcontrol Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qinputcontrol
|
||||
SOURCES
|
||||
tst_qinputcontrol.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
143
tests/auto/gui/text/qinputcontrol/tst_qinputcontrol.cpp
Normal file
143
tests/auto/gui/text/qinputcontrol/tst_qinputcontrol.cpp
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <private/qinputcontrol_p.h>
|
||||
#include <QtGui/QKeyEvent>
|
||||
|
||||
class tst_QInputControl: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
private slots:
|
||||
void isAcceptableInput_data();
|
||||
void isAcceptableInput();
|
||||
void tabOnlyAcceptableInputForTextEdit();
|
||||
};
|
||||
|
||||
void tst_QInputControl::isAcceptableInput_data()
|
||||
{
|
||||
QTest::addColumn<QString>("text");
|
||||
QTest::addColumn<Qt::KeyboardModifiers>("modifiers");
|
||||
QTest::addColumn<bool>("acceptable");
|
||||
|
||||
QTest::newRow("empty-string") << QString() << Qt::KeyboardModifiers() << false;
|
||||
QTest::newRow("zwnj") << QString(QChar(0x200C)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("zwnj-with-ctrl") << QString(QChar(0x200C)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("zwnj-with-ctrl-shift") << QString(QChar(0x200C)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("zwj") << QString(QChar(0x200D)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("zwj-with-ctrl") << QString(QChar(0x200D)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("zwj-with-ctrl-shift") << QString(QChar(0x200D)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("printable-latin") << QString(QLatin1Char('a')) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("printable-latin-with-ctrl") << QString(QLatin1Char('a')) << Qt::KeyboardModifiers(Qt::ControlModifier) << false;
|
||||
QTest::newRow("printable-latin-with-ctrl-shift") << QString(QLatin1Char('a')) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << false;
|
||||
QTest::newRow("printable-hebrew") << QString(QChar(0x2135)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("private-use-area") << QString(QChar(0xE832)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("good-surrogate-0") << QString::fromUtf16(u"\U0001F44D") << Qt::KeyboardModifiers() << true;
|
||||
{
|
||||
const QChar data[] = { QChar(0xD800), QChar(0xDC00) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("good-surrogate-1") << str << Qt::KeyboardModifiers() << true;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xD800), QChar(0xDFFF) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("good-surrogate-2") << str << Qt::KeyboardModifiers() << true;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xDBFF), QChar(0xDC00) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("good-surrogate-3") << str << Qt::KeyboardModifiers() << true;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xDBFF), QChar(0xDFFF) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("good-surrogate-4") << str << Qt::KeyboardModifiers() << true;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xD7FF), QChar(0xDC00) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("bad-surrogate-1") << str << Qt::KeyboardModifiers() << false;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xD7FF), QChar(0xDFFF) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("bad-surrogate-2") << str << Qt::KeyboardModifiers() << false;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xDC00), QChar(0xDC00) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("bad-surrogate-3") << str << Qt::KeyboardModifiers() << false;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xD800), QChar(0xE000) };
|
||||
const QString str = QString(data, 2);
|
||||
QTest::newRow("bad-surrogate-4") << str << Qt::KeyboardModifiers() << false;
|
||||
}
|
||||
{
|
||||
const QChar data[] = { QChar(0xD800) };
|
||||
const QString str = QString(data, 1);
|
||||
QTest::newRow("bad-surrogate-5") << str << Qt::KeyboardModifiers() << false;
|
||||
}
|
||||
QTest::newRow("multiple-printable") << QStringLiteral("foobar") << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("rlm") << QString(QChar(0x200F)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("rlm-with-ctrl") << QString(QChar(0x200F)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("rlm-with-ctrl-shift") << QString(QChar(0x200F)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("lrm") << QString(QChar(0x200E)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("lrm-with-ctrl") << QString(QChar(0x200E)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("lrm-with-ctrl-shift") << QString(QChar(0x200E)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("rlo") << QString(QChar(0x202E)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("rlo-with-ctrl") << QString(QChar(0x202E)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("rlo-with-ctrl-shift") << QString(QChar(0x202E)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("lro") << QString(QChar(0x202D)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("lro-with-ctrl") << QString(QChar(0x202D)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("lro-with-ctrl-shift") << QString(QChar(0x202D)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("lre") << QString(QChar(0x202B)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("lre-with-ctrl") << QString(QChar(0x202B)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("lre-with-ctrl-shift") << QString(QChar(0x202B)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("rle") << QString(QChar(0x202A)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("rle-with-ctrl") << QString(QChar(0x202A)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("rle-with-ctrl-shift") << QString(QChar(0x202A)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
QTest::newRow("pdf") << QString(QChar(0x202C)) << Qt::KeyboardModifiers() << true;
|
||||
QTest::newRow("pdf-with-ctrl") << QString(QChar(0x202C)) << Qt::KeyboardModifiers(Qt::ControlModifier) << true;
|
||||
QTest::newRow("pdf-with-ctrl-shift") << QString(QChar(0x202C)) << Qt::KeyboardModifiers(Qt::ControlModifier | Qt::ShiftModifier) << true;
|
||||
|
||||
}
|
||||
|
||||
void tst_QInputControl::isAcceptableInput()
|
||||
{
|
||||
QFETCH(QString, text);
|
||||
QFETCH(Qt::KeyboardModifiers, modifiers);
|
||||
QFETCH(bool, acceptable);
|
||||
|
||||
QKeyEvent keyEvent(QKeyEvent::KeyPress, Qt::Key_unknown, modifiers, text);
|
||||
|
||||
{
|
||||
QInputControl inputControl(QInputControl::TextEdit);
|
||||
QCOMPARE(inputControl.isAcceptableInput(&keyEvent), acceptable);
|
||||
}
|
||||
|
||||
{
|
||||
QInputControl inputControl(QInputControl::LineEdit);
|
||||
QCOMPARE(inputControl.isAcceptableInput(&keyEvent), acceptable);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QInputControl::tabOnlyAcceptableInputForTextEdit()
|
||||
{
|
||||
QKeyEvent keyEvent(QKeyEvent::KeyPress, Qt::Key_unknown, Qt::KeyboardModifiers(), QLatin1String("\t"));
|
||||
|
||||
{
|
||||
QInputControl inputControl(QInputControl::TextEdit);
|
||||
QCOMPARE(inputControl.isAcceptableInput(&keyEvent), true);
|
||||
}
|
||||
|
||||
{
|
||||
QInputControl inputControl(QInputControl::LineEdit);
|
||||
QCOMPARE(inputControl.isAcceptableInput(&keyEvent), false);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QInputControl)
|
||||
#include "tst_qinputcontrol.moc"
|
||||
|
28
tests/auto/gui/text/qrawfont/CMakeLists.txt
Normal file
28
tests/auto/gui/text/qrawfont/CMakeLists.txt
Normal file
@ -0,0 +1,28 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qrawfont Test:
|
||||
#####################################################################
|
||||
|
||||
# Resources:
|
||||
set_source_files_properties("../../../shared/resources/testfont.ttf"
|
||||
PROPERTIES QT_RESOURCE_ALIAS "testfont.ttf"
|
||||
)
|
||||
set(testdata_resource_files
|
||||
"../../../shared/resources/testfont.ttf"
|
||||
"testfont_bold_italic.ttf"
|
||||
"testfont_os2_v1.ttf"
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qrawfont
|
||||
SOURCES
|
||||
tst_qrawfont.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
TESTDATA ${testdata_resource_files}
|
||||
BUILTIN_TESTDATA
|
||||
)
|
||||
|
BIN
tests/auto/gui/text/qrawfont/testfont_bold_italic.ttf
Normal file
BIN
tests/auto/gui/text/qrawfont/testfont_bold_italic.ttf
Normal file
Binary file not shown.
BIN
tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf
Normal file
BIN
tests/auto/gui/text/qrawfont/testfont_os2_v1.ttf
Normal file
Binary file not shown.
1084
tests/auto/gui/text/qrawfont/tst_qrawfont.cpp
Normal file
1084
tests/auto/gui/text/qrawfont/tst_qrawfont.cpp
Normal file
File diff suppressed because it is too large
Load Diff
22
tests/auto/gui/text/qstatictext/CMakeLists.txt
Normal file
22
tests/auto/gui/text/qstatictext/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qstatictext Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qstatictext
|
||||
SOURCES
|
||||
tst_qstatictext.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qstatictext CONDITION QT_FEATURE_private_tests
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::GuiPrivate
|
||||
)
|
882
tests/auto/gui/text/qstatictext/tst_qstatictext.cpp
Normal file
882
tests/auto/gui/text/qstatictext/tst_qstatictext.cpp
Normal file
@ -0,0 +1,882 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#include <QtCore/QSet>
|
||||
#include <QtGui/QGuiApplication>
|
||||
#include <QtGui/QPainter>
|
||||
#include <QtGui/QImage>
|
||||
|
||||
#include <qstatictext.h>
|
||||
#include <qpaintengine.h>
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
#include <private/qstatictext_p.h>
|
||||
#endif
|
||||
|
||||
// #define DEBUG_SAVE_IMAGE
|
||||
|
||||
static inline QImage blankSquare()
|
||||
{
|
||||
// a "blank" square; we compare against in our testfunctions to verify
|
||||
// that we have actually painted something
|
||||
QPixmap pm(1000, 1000);
|
||||
pm.fill(Qt::white);
|
||||
return pm.toImage();
|
||||
}
|
||||
|
||||
class tst_QStaticText: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
tst_QStaticText() : m_whiteSquare(blankSquare()) {}
|
||||
|
||||
private slots:
|
||||
void constructionAndDestruction();
|
||||
void drawToPoint_data();
|
||||
void drawToPoint();
|
||||
void drawToRect_data();
|
||||
void drawToRect();
|
||||
void compareToDrawText_data();
|
||||
void compareToDrawText();
|
||||
void setFont();
|
||||
void setTextWidth();
|
||||
void prepareToCorrectData();
|
||||
void prepareToWrongData();
|
||||
|
||||
void copyConstructor();
|
||||
|
||||
void translatedPainter();
|
||||
void rotatedPainter();
|
||||
void scaledPainter();
|
||||
void projectedPainter();
|
||||
#if 0
|
||||
void rotatedScaledAndTranslatedPainter_data();
|
||||
void rotatedScaledAndTranslatedPainter();
|
||||
#endif
|
||||
void transformationChanged();
|
||||
|
||||
void plainTextVsRichText();
|
||||
|
||||
void setPenPlainText_data();
|
||||
void setPenPlainText();
|
||||
void setPenRichText();
|
||||
void richTextOverridesPen();
|
||||
|
||||
void unprintableCharacter_qtbug12614();
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
void underlinedColor_qtbug20159();
|
||||
void textDocumentColor();
|
||||
#endif
|
||||
|
||||
void multiLine();
|
||||
|
||||
void size_qtbug65836();
|
||||
|
||||
private:
|
||||
bool supportsTransformations() const;
|
||||
|
||||
const QImage m_whiteSquare;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(QImage::Format);
|
||||
|
||||
void tst_QStaticText::constructionAndDestruction()
|
||||
{
|
||||
QStaticText text("My text");
|
||||
}
|
||||
|
||||
void tst_QStaticText::copyConstructor()
|
||||
{
|
||||
QStaticText text(QLatin1String("My text"));
|
||||
|
||||
QTextOption textOption(Qt::AlignRight);
|
||||
text.setTextOption(textOption);
|
||||
|
||||
text.setPerformanceHint(QStaticText::AggressiveCaching);
|
||||
text.setTextWidth(123.456);
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
QStaticText copiedText(text);
|
||||
copiedText.setText(QLatin1String("Other text"));
|
||||
|
||||
QCOMPARE(copiedText.textOption().alignment(), Qt::AlignRight);
|
||||
QCOMPARE(copiedText.performanceHint(), QStaticText::AggressiveCaching);
|
||||
QCOMPARE(copiedText.textWidth(), 123.456);
|
||||
QCOMPARE(copiedText.textFormat(), Qt::PlainText);
|
||||
|
||||
QStaticText otherCopiedText(copiedText);
|
||||
otherCopiedText.setTextWidth(789);
|
||||
|
||||
QCOMPARE(otherCopiedText.text(), QString::fromLatin1("Other text"));
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QStaticText::PerformanceHint)
|
||||
void tst_QStaticText::drawToPoint_data()
|
||||
{
|
||||
QTest::addColumn<QStaticText::PerformanceHint>("performanceHint");
|
||||
|
||||
QTest::newRow("Moderate caching") << QStaticText::ModerateCaching;
|
||||
QTest::newRow("Aggressive caching") << QStaticText::AggressiveCaching;
|
||||
}
|
||||
|
||||
void tst_QStaticText::drawToPoint()
|
||||
{
|
||||
QFETCH(QStaticText::PerformanceHint, performanceHint);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
text.setPerformanceHint(performanceHint);
|
||||
p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
|
||||
}
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::drawToRect_data()
|
||||
{
|
||||
QTest::addColumn<QStaticText::PerformanceHint>("performanceHint");
|
||||
|
||||
QTest::newRow("Moderate caching") << QStaticText::ModerateCaching;
|
||||
QTest::newRow("Aggressive caching") << QStaticText::AggressiveCaching;
|
||||
}
|
||||
|
||||
void tst_QStaticText::drawToRect()
|
||||
{
|
||||
QFETCH(QStaticText::PerformanceHint, performanceHint);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.drawText(QRectF(11, 12, 10, 500), "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextWidth(10),
|
||||
p.setClipRect(QRectF(11, 12, 10, 500));
|
||||
text.setPerformanceHint(performanceHint);
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
p.drawStaticText(QPointF(11, 12), text);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imageDrawText.save("drawToRect_imageDrawText.png");
|
||||
imageDrawStaticText.save("drawToRect_imageDrawStaticText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::compareToDrawText_data()
|
||||
{
|
||||
QTest::addColumn<QFont>("font");
|
||||
|
||||
QTest::newRow("default") << QFont();
|
||||
QFont sansserif; sansserif.setStyleHint(QFont::SansSerif);
|
||||
QFont serif; serif.setStyleHint(QFont::Serif);
|
||||
QFont monospace; monospace.setStyleHint(QFont::Monospace);
|
||||
QTest::newRow("sans-serif") << QFont(sansserif.defaultFamily());
|
||||
QTest::newRow("serif") << QFont(serif.defaultFamily());
|
||||
QTest::newRow("monospace") << QFont(monospace.defaultFamily());
|
||||
}
|
||||
|
||||
void tst_QStaticText::compareToDrawText()
|
||||
{
|
||||
QFETCH(QFont, font);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.setFont(font);
|
||||
p.drawText(QRectF(11, 12, 30, 500), "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticPlainText(1000, 1000);
|
||||
imageDrawStaticPlainText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticPlainText);
|
||||
p.setFont(font);
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextWidth(10),
|
||||
p.setClipRect(QRectF(11, 12, 30, 500));
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
p.drawStaticText(QPointF(11, 12), text);
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticRichText(1000, 1000);
|
||||
imageDrawStaticRichText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticRichText);
|
||||
p.setFont(font);
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextWidth(10),
|
||||
p.setClipRect(QRectF(11, 12, 30, 500));
|
||||
text.setTextFormat(Qt::RichText);
|
||||
p.drawStaticText(QPointF(11, 12), text);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imageDrawText.save("compareToDrawText_imageDrawText.png");
|
||||
imageDrawStaticPlainText.save("compareToDrawText_imageDrawStaticPlainText.png");
|
||||
imageDrawStaticRichText.save("compareToDrawText_imageDrawStaticRichText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imageDrawStaticPlainText, imageDrawText);
|
||||
QCOMPARE(imageDrawStaticRichText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::prepareToCorrectData()
|
||||
{
|
||||
QTransform transform;
|
||||
transform.scale(2.0, 2.0);
|
||||
transform.translate(100, 10);
|
||||
transform.rotate(90, Qt::ZAxis);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.setTransform(transform);
|
||||
p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
p.setTransform(transform);
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.prepare(transform, p.font());
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imageDrawText.save("prepareToCorrectData_imageDrawText.png");
|
||||
imageDrawStaticText.save("prepareToCorrectData_imageDrawStaticText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
|
||||
if (!supportsTransformations())
|
||||
QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::prepareToWrongData()
|
||||
{
|
||||
QTransform transform;
|
||||
transform.scale(2.0, 2.0);
|
||||
transform.rotate(90, Qt::ZAxis);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.prepare(transform, p.font());
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
|
||||
}
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
|
||||
void tst_QStaticText::setFont()
|
||||
{
|
||||
QFont font = QGuiApplication::font();
|
||||
font.setBold(true);
|
||||
font.setPointSize(28);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
|
||||
p.setFont(font);
|
||||
p.drawText(QRectF(11, 120, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
|
||||
QStaticText text;
|
||||
text.setText("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
p.drawStaticText(0, 0, text);
|
||||
|
||||
p.setFont(font);
|
||||
p.drawStaticText(11, 120, text);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imageDrawText.save("setFont_imageDrawText.png");
|
||||
imageDrawStaticText.save("setFont_imageDrawStaticText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::setTextWidth()
|
||||
{
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.drawText(QRectF(11, 12, 10, 500), "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextWidth(10);
|
||||
p.setClipRect(QRectF(11, 12, 10, 500));
|
||||
p.drawStaticText(QPointF(11, 12), text);
|
||||
}
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::translatedPainter()
|
||||
{
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.translate(100, 200);
|
||||
|
||||
p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
p.translate(100, 200);
|
||||
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
|
||||
}
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
bool tst_QStaticText::supportsTransformations() const
|
||||
{
|
||||
QPixmap pm(10, 10);
|
||||
QPainter p(&pm);
|
||||
return p.paintEngine()->type() != QPaintEngine::OpenGL;
|
||||
}
|
||||
|
||||
void tst_QStaticText::rotatedPainter()
|
||||
{
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.rotate(30.0);
|
||||
p.drawText(QRectF(0, 0, 1000, 100), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
QPainter p(&imageDrawStaticText);
|
||||
p.rotate(30.0);
|
||||
p.drawStaticText(QPoint(0, 0), text);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imageDrawText.save("rotatedPainter_imageDrawText.png");
|
||||
imageDrawStaticText.save("rotatedPainter_imageDrawStaticText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
|
||||
if (!supportsTransformations())
|
||||
QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::scaledPainter()
|
||||
{
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.scale(2.0, 0.2);
|
||||
|
||||
p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
p.scale(2.0, 0.2);
|
||||
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
|
||||
}
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
|
||||
if (!supportsTransformations())
|
||||
QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::projectedPainter()
|
||||
{
|
||||
QTransform transform;
|
||||
transform.rotate(90, Qt::XAxis);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.setTransform(transform);
|
||||
|
||||
p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
p.setTransform(transform);
|
||||
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
|
||||
}
|
||||
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
#if 0
|
||||
void tst_QStaticText::rotatedScaledAndTranslatedPainter_data()
|
||||
{
|
||||
QTest::addColumn<qreal>("offset");
|
||||
|
||||
for (int i=0; i<100; ++i) {
|
||||
qreal offset = 300 + i / 100.;
|
||||
QTest::newRow(QByteArray::number(offset).constData()) << offset;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QStaticText::rotatedScaledAndTranslatedPainter()
|
||||
{
|
||||
QFETCH(qreal, offset);
|
||||
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.translate(offset, 0);
|
||||
p.rotate(45.0);
|
||||
p.scale(2.0, 2.0);
|
||||
p.translate(100, 200);
|
||||
|
||||
p.drawText(11, 12, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
p.translate(offset, 0);
|
||||
p.rotate(45.0);
|
||||
p.scale(2.0, 2.0);
|
||||
p.translate(100, 200);
|
||||
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
p.drawStaticText(QPointF(11, 12 - QFontMetricsF(p.font()).ascent()), text);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imageDrawText.save("rotatedScaledAndPainter_imageDrawText.png");
|
||||
imageDrawStaticText.save("rotatedScaledAndPainter_imageDrawStaticText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
|
||||
if (!supportsTransformations())
|
||||
QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QStaticText::transformationChanged()
|
||||
{
|
||||
QPixmap imageDrawText(1000, 1000);
|
||||
imageDrawText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawText);
|
||||
p.rotate(33.0);
|
||||
p.scale(0.5, 0.7);
|
||||
|
||||
p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
|
||||
p.scale(2.0, 2.5);
|
||||
p.drawText(QRectF(0, 0, 1000, 1000), 0, "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
}
|
||||
|
||||
QPixmap imageDrawStaticText(1000, 1000);
|
||||
imageDrawStaticText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageDrawStaticText);
|
||||
p.rotate(33.0);
|
||||
p.scale(0.5, 0.7);
|
||||
|
||||
QStaticText text("Lorem ipsum dolor sit amet, consectetur adipiscing elit.");
|
||||
text.setTextFormat(Qt::PlainText);
|
||||
|
||||
p.drawStaticText(QPointF(0, 0), text);
|
||||
|
||||
p.scale(2.0, 2.5);
|
||||
p.drawStaticText(QPointF(0, 0), text);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imageDrawText.save("transformationChanged_imageDrawText.png");
|
||||
imageDrawStaticText.save("transformationChanged_imageDrawStaticText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imageDrawText.toImage() != m_whiteSquare);
|
||||
|
||||
if (!supportsTransformations())
|
||||
QEXPECT_FAIL("", "Graphics system does not support transformed text on this platform", Abort);
|
||||
QCOMPARE(imageDrawStaticText, imageDrawText);
|
||||
}
|
||||
|
||||
void tst_QStaticText::plainTextVsRichText()
|
||||
{
|
||||
QPixmap imagePlainText(1000, 1000);
|
||||
imagePlainText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imagePlainText);
|
||||
|
||||
QStaticText staticText;
|
||||
staticText.setText("FOObar");
|
||||
staticText.setTextFormat(Qt::PlainText);
|
||||
|
||||
p.drawStaticText(10, 10, staticText);
|
||||
}
|
||||
|
||||
QPixmap imageRichText(1000, 1000);
|
||||
imageRichText.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&imageRichText);
|
||||
|
||||
QStaticText staticText;
|
||||
staticText.setText("<html><body>FOObar</body></html>");
|
||||
staticText.setTextFormat(Qt::RichText);
|
||||
|
||||
p.drawStaticText(10, 10, staticText);
|
||||
}
|
||||
|
||||
#if defined(DEBUG_SAVE_IMAGE)
|
||||
imagePlainText.save("plainTextVsRichText_imagePlainText.png");
|
||||
imageRichText.save("plainTextVsRichText_imageRichText.png");
|
||||
#endif
|
||||
|
||||
QVERIFY(imagePlainText.toImage() != m_whiteSquare);
|
||||
QCOMPARE(imagePlainText, imageRichText);
|
||||
}
|
||||
|
||||
static bool checkPixels(const QImage &image,
|
||||
Qt::GlobalColor expectedColor1, Qt::GlobalColor expectedColor2,
|
||||
QByteArray *errorMessage)
|
||||
{
|
||||
const QRgb expectedRgb1 = QColor(expectedColor1).rgba();
|
||||
const QRgb expectedRgb2 = QColor(expectedColor2).rgba();
|
||||
|
||||
for (int x = 0, w = image.width(); x < w; ++x) {
|
||||
for (int y = 0, h = image.height(); y < h; ++y) {
|
||||
const QRgb pixel = image.pixel(x, y);
|
||||
if (pixel != expectedRgb1 && pixel != expectedRgb2) {
|
||||
QString message;
|
||||
QDebug(&message) << "Color mismatch in image" << image
|
||||
<< "at" << x << ',' << y << ':' << Qt::showbase << Qt::hex << pixel
|
||||
<< "(expected: " << expectedRgb1 << ',' << expectedRgb2 << ')';
|
||||
*errorMessage = message.toLocal8Bit();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void tst_QStaticText::setPenPlainText_data()
|
||||
{
|
||||
QTest::addColumn<QImage::Format>("format");
|
||||
|
||||
QTest::newRow("argb32pm") << QImage::Format_ARGB32_Premultiplied;
|
||||
QTest::newRow("rgb32") << QImage::Format_RGB32;
|
||||
QTest::newRow("rgba8888pm") << QImage::Format_RGBA8888_Premultiplied;
|
||||
QTest::newRow("rgbx8888") << QImage::Format_RGBX8888;
|
||||
}
|
||||
|
||||
void tst_QStaticText::setPenPlainText()
|
||||
{
|
||||
QFETCH(QImage::Format, format);
|
||||
|
||||
QFont font = QGuiApplication::font();
|
||||
font.setStyleStrategy(QFont::NoAntialias);
|
||||
|
||||
QFontMetricsF fm(font);
|
||||
QImage image(qCeil(fm.horizontalAdvance("XXXXX")), qCeil(fm.height()), format);
|
||||
image.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&image);
|
||||
p.setFont(font);
|
||||
p.setPen(Qt::yellow);
|
||||
|
||||
QStaticText staticText("XXXXX");
|
||||
staticText.setTextFormat(Qt::PlainText);
|
||||
p.drawStaticText(0, 0, staticText);
|
||||
}
|
||||
|
||||
QByteArray errorMessage;
|
||||
QVERIFY2(checkPixels(image, Qt::yellow, Qt::white, &errorMessage),
|
||||
errorMessage.constData());
|
||||
}
|
||||
|
||||
void tst_QStaticText::setPenRichText()
|
||||
{
|
||||
QFont font = QGuiApplication::font();
|
||||
font.setStyleStrategy(QFont::NoAntialias);
|
||||
|
||||
QFontMetricsF fm(font);
|
||||
QPixmap image(qCeil(fm.horizontalAdvance("XXXXX")), qCeil(fm.height()));
|
||||
image.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&image);
|
||||
p.setFont(font);
|
||||
p.setPen(Qt::green);
|
||||
|
||||
QStaticText staticText;
|
||||
staticText.setText("<html><body>XXXXX</body></html>");
|
||||
staticText.setTextFormat(Qt::RichText);
|
||||
p.drawStaticText(0, 0, staticText);
|
||||
}
|
||||
|
||||
QByteArray errorMessage;
|
||||
QVERIFY2(checkPixels(image.toImage(), Qt::green, Qt::white, &errorMessage),
|
||||
errorMessage.constData());
|
||||
}
|
||||
|
||||
void tst_QStaticText::richTextOverridesPen()
|
||||
{
|
||||
QFont font = QGuiApplication::font();
|
||||
font.setStyleStrategy(QFont::NoAntialias);
|
||||
|
||||
QFontMetricsF fm(font);
|
||||
QPixmap image(qCeil(fm.horizontalAdvance("XXXXX")), qCeil(fm.height()));
|
||||
image.fill(Qt::white);
|
||||
{
|
||||
QPainter p(&image);
|
||||
p.setFont(font);
|
||||
p.setPen(Qt::green);
|
||||
|
||||
QStaticText staticText;
|
||||
staticText.setText("<html><body><font color=\"#ff0000\">XXXXX</font></body></html>");
|
||||
staticText.setTextFormat(Qt::RichText);
|
||||
p.drawStaticText(0, 0, staticText);
|
||||
}
|
||||
|
||||
QByteArray errorMessage;
|
||||
QVERIFY2(checkPixels(image.toImage(), Qt::red, Qt::white, &errorMessage),
|
||||
errorMessage.constData());
|
||||
}
|
||||
|
||||
void tst_QStaticText::unprintableCharacter_qtbug12614()
|
||||
{
|
||||
QString s(QChar(0x200B)); // U+200B, ZERO WIDTH SPACE
|
||||
|
||||
QStaticText staticText(s);
|
||||
|
||||
QVERIFY(staticText.size().isValid()); // Force layout. Should not crash.
|
||||
}
|
||||
|
||||
#ifdef QT_BUILD_INTERNAL
|
||||
void tst_QStaticText::underlinedColor_qtbug20159()
|
||||
{
|
||||
QString multiScriptText;
|
||||
multiScriptText += QChar(0x0410); // Cyrillic 'A'
|
||||
multiScriptText += QLatin1Char('A');
|
||||
|
||||
QStaticText staticText(multiScriptText);
|
||||
|
||||
QFont font;
|
||||
font.setUnderline(true);
|
||||
|
||||
staticText.prepare(QTransform(), font);
|
||||
|
||||
QStaticTextPrivate *d = QStaticTextPrivate::get(&staticText);
|
||||
QCOMPARE(d->itemCount, 2);
|
||||
|
||||
// The pen should not be marked as dirty when drawing the underline
|
||||
QVERIFY(!d->items[0].color.isValid());
|
||||
QVERIFY(!d->items[1].color.isValid());
|
||||
}
|
||||
|
||||
void tst_QStaticText::textDocumentColor()
|
||||
{
|
||||
QStaticText staticText("A<font color=\"red\">B</font>");
|
||||
staticText.setTextFormat(Qt::RichText);
|
||||
staticText.prepare();
|
||||
|
||||
QStaticTextPrivate *d = QStaticTextPrivate::get(&staticText);
|
||||
QCOMPARE(d->itemCount, 2);
|
||||
|
||||
// The pen should not be marked as dirty when drawing the underline
|
||||
QVERIFY(!d->items[0].color.isValid());
|
||||
QVERIFY(d->items[1].color.isValid());
|
||||
|
||||
QCOMPARE(d->items[1].color, QColor(Qt::red));
|
||||
}
|
||||
#endif
|
||||
|
||||
class TestPaintEngine: public QPaintEngine
|
||||
{
|
||||
public:
|
||||
void drawTextItem(const QPointF &p, const QTextItem &) override
|
||||
{
|
||||
differentVerticalPositions.insert(qRound(p.y()));
|
||||
}
|
||||
|
||||
void updateState(const QPaintEngineState &) override {}
|
||||
|
||||
void drawPolygon(const QPointF *, int , PolygonDrawMode ) override {}
|
||||
|
||||
bool begin(QPaintDevice *) override { return true; }
|
||||
bool end() override { return true; }
|
||||
void drawPixmap(const QRectF &, const QPixmap &, const QRectF &) override {}
|
||||
Type type() const override
|
||||
{
|
||||
return User;
|
||||
}
|
||||
|
||||
QSet<int> differentVerticalPositions;
|
||||
};
|
||||
|
||||
class TestPixmap: public QPixmap
|
||||
{
|
||||
public:
|
||||
TestPixmap(int w, int h) : QPixmap(w, h), testPaintEngine(new TestPaintEngine) {}
|
||||
~TestPixmap() { delete testPaintEngine; }
|
||||
|
||||
QPaintEngine *paintEngine() const override
|
||||
{
|
||||
return testPaintEngine;
|
||||
}
|
||||
|
||||
TestPaintEngine *testPaintEngine;
|
||||
};
|
||||
|
||||
void tst_QStaticText::multiLine()
|
||||
{
|
||||
TestPixmap pixmap(100, 100);
|
||||
|
||||
TestPaintEngine *paintEngine = pixmap.testPaintEngine;
|
||||
|
||||
{
|
||||
QPainter p(&pixmap);
|
||||
QStaticText text;
|
||||
text.setText(QLatin1String("line 1") + QChar(QChar::LineSeparator) + QLatin1String("line 2"));
|
||||
p.drawStaticText(0, 0, text);
|
||||
}
|
||||
|
||||
QCOMPARE(paintEngine->differentVerticalPositions.size(), 2);
|
||||
}
|
||||
|
||||
void tst_QStaticText::size_qtbug65836()
|
||||
{
|
||||
const QString text = QLatin1String("Lorem ipsum dolor sit amet, "
|
||||
"consectetur adipiscing elit.");
|
||||
QFont font("Courier");
|
||||
font.setPixelSize(15);
|
||||
|
||||
{
|
||||
QStaticText st1(text);
|
||||
st1.setTextFormat(Qt::PlainText);
|
||||
st1.prepare(QTransform(), font);
|
||||
|
||||
QStaticText st2(text);
|
||||
st2.setTextFormat(Qt::RichText);
|
||||
QTextOption opt;
|
||||
opt.setWrapMode(QTextOption::NoWrap);
|
||||
st2.setTextOption(opt);
|
||||
st2.prepare(QTransform(), font);
|
||||
|
||||
QCOMPARE(st1.size(), st2.size());
|
||||
}
|
||||
|
||||
{
|
||||
QStaticText st1(text);
|
||||
st1.setTextFormat(Qt::PlainText);
|
||||
st1.setTextWidth(10.0);
|
||||
st1.prepare(QTransform(), font);
|
||||
|
||||
QStaticText st2(text);
|
||||
st2.setTextFormat(Qt::RichText);
|
||||
st2.setTextWidth(10.0);
|
||||
st2.prepare(QTransform(), font);
|
||||
|
||||
QCOMPARE(st1.size(), st2.size());
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QStaticText)
|
||||
#include "tst_qstatictext.moc"
|
21
tests/auto/gui/text/qsyntaxhighlighter/CMakeLists.txt
Normal file
21
tests/auto/gui/text/qsyntaxhighlighter/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qsyntaxhighlighter Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qsyntaxhighlighter
|
||||
SOURCES
|
||||
tst_qsyntaxhighlighter.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qsyntaxhighlighter CONDITION TARGET Qt::Widgets
|
||||
LIBRARIES
|
||||
Qt::Widgets
|
||||
)
|
@ -0,0 +1,548 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QTextDocument>
|
||||
#include <QTextLayout>
|
||||
#include <QDebug>
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <QSyntaxHighlighter>
|
||||
#include <QSignalSpy>
|
||||
|
||||
#ifndef QT_NO_WIDGETS
|
||||
#include <QTextEdit>
|
||||
#endif
|
||||
|
||||
class QTestDocumentLayout : public QAbstractTextDocumentLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
inline QTestDocumentLayout(QTextDocument *doc)
|
||||
: QAbstractTextDocumentLayout(doc), documentChangedCalled(false) {}
|
||||
|
||||
virtual void draw(QPainter *, const QAbstractTextDocumentLayout::PaintContext &) override {}
|
||||
|
||||
virtual int hitTest(const QPointF &, Qt::HitTestAccuracy ) const override { return 0; }
|
||||
|
||||
virtual void documentChanged(int, int, int) override { documentChangedCalled = true; }
|
||||
|
||||
virtual int pageCount() const override { return 1; }
|
||||
|
||||
virtual QSizeF documentSize() const override { return QSize(); }
|
||||
|
||||
virtual QRectF frameBoundingRect(QTextFrame *) const override { return QRectF(); }
|
||||
virtual QRectF blockBoundingRect(const QTextBlock &) const override { return QRectF(); }
|
||||
|
||||
bool documentChangedCalled;
|
||||
};
|
||||
|
||||
class tst_QSyntaxHighlighter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
void basic();
|
||||
void basicTwo();
|
||||
void removeFormatsOnDelete();
|
||||
void emptyBlocks();
|
||||
void setCharFormat();
|
||||
void highlightOnInit();
|
||||
void highlightOnInitAndAppend();
|
||||
void stopHighlightingWhenStateDoesNotChange();
|
||||
void unindent();
|
||||
void highlightToEndOfDocument();
|
||||
void highlightToEndOfDocument2();
|
||||
void preservePreeditArea();
|
||||
void task108530();
|
||||
void avoidUnnecessaryRehighlight();
|
||||
void avoidUnnecessaryDelayedRehighlight();
|
||||
void noContentsChangedDuringHighlight();
|
||||
void rehighlight();
|
||||
void rehighlightBlock();
|
||||
#ifndef QT_NO_WIDGETS
|
||||
void textEditParent();
|
||||
#endif
|
||||
|
||||
private:
|
||||
QTextDocument *doc;
|
||||
QTestDocumentLayout *lout;
|
||||
QTextCursor cursor;
|
||||
};
|
||||
|
||||
void tst_QSyntaxHighlighter::init()
|
||||
{
|
||||
doc = new QTextDocument;
|
||||
lout = new QTestDocumentLayout(doc);
|
||||
doc->setDocumentLayout(lout);
|
||||
cursor = QTextCursor(doc);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::cleanup()
|
||||
{
|
||||
delete doc;
|
||||
doc = 0;
|
||||
}
|
||||
|
||||
class TestHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
public:
|
||||
inline TestHighlighter(const QList<QTextLayout::FormatRange> &fmts, QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent), formats(fmts), highlighted(false), callCount(0)
|
||||
{
|
||||
}
|
||||
inline TestHighlighter(QObject *parent)
|
||||
: QSyntaxHighlighter(parent) {}
|
||||
inline TestHighlighter(QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent), highlighted(false), callCount(0) {}
|
||||
|
||||
virtual void highlightBlock(const QString &text) override
|
||||
{
|
||||
for (int i = 0; i < formats.size(); ++i) {
|
||||
const QTextLayout::FormatRange &range = formats.at(i);
|
||||
setFormat(range.start, range.length, range.format);
|
||||
}
|
||||
highlighted = true;
|
||||
highlightedText += text;
|
||||
++callCount;
|
||||
}
|
||||
|
||||
QList<QTextLayout::FormatRange> formats;
|
||||
bool highlighted;
|
||||
int callCount;
|
||||
QString highlightedText;
|
||||
};
|
||||
|
||||
void tst_QSyntaxHighlighter::basic()
|
||||
{
|
||||
QList<QTextLayout::FormatRange> formats;
|
||||
QTextLayout::FormatRange range;
|
||||
range.start = 0;
|
||||
range.length = 2;
|
||||
range.format.setForeground(Qt::blue);
|
||||
formats.append(range);
|
||||
|
||||
range.start = 4;
|
||||
range.length = 2;
|
||||
range.format.setFontItalic(true);
|
||||
formats.append(range);
|
||||
|
||||
range.start = 9;
|
||||
range.length = 2;
|
||||
range.format.setFontUnderline(true);
|
||||
formats.append(range);
|
||||
|
||||
TestHighlighter *hl = new TestHighlighter(formats, doc);
|
||||
|
||||
lout->documentChangedCalled = false;
|
||||
doc->setPlainText("Hello World");
|
||||
QVERIFY(hl->highlighted);
|
||||
QVERIFY(lout->documentChangedCalled);
|
||||
|
||||
QCOMPARE(doc->begin().layout()->formats(), formats);
|
||||
}
|
||||
|
||||
class CommentTestHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
public:
|
||||
inline CommentTestHighlighter(QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent), highlighted(false) {}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
highlighted = false;
|
||||
}
|
||||
|
||||
virtual void highlightBlock(const QString &text) override
|
||||
{
|
||||
QTextCharFormat commentFormat;
|
||||
commentFormat.setForeground(Qt::darkGreen);
|
||||
commentFormat.setFontWeight(QFont::StyleItalic);
|
||||
commentFormat.setFontFixedPitch(true);
|
||||
int textLength = text.size();
|
||||
|
||||
if (text.startsWith(QLatin1Char(';'))){
|
||||
// The entire line is a comment
|
||||
setFormat(0, textLength, commentFormat);
|
||||
highlighted = true;
|
||||
}
|
||||
}
|
||||
bool highlighted;
|
||||
};
|
||||
|
||||
|
||||
void tst_QSyntaxHighlighter::basicTwo()
|
||||
{
|
||||
// Done for task 104409
|
||||
CommentTestHighlighter *hl = new CommentTestHighlighter(doc);
|
||||
doc->setPlainText("; a test");
|
||||
QVERIFY(hl->highlighted);
|
||||
QVERIFY(lout->documentChangedCalled);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::removeFormatsOnDelete()
|
||||
{
|
||||
QList<QTextLayout::FormatRange> formats;
|
||||
QTextLayout::FormatRange range;
|
||||
range.start = 0;
|
||||
range.length = 9;
|
||||
range.format.setForeground(Qt::blue);
|
||||
formats.append(range);
|
||||
|
||||
TestHighlighter *hl = new TestHighlighter(formats, doc);
|
||||
|
||||
lout->documentChangedCalled = false;
|
||||
doc->setPlainText("Hello World");
|
||||
QVERIFY(hl->highlighted);
|
||||
QVERIFY(lout->documentChangedCalled);
|
||||
|
||||
lout->documentChangedCalled = false;
|
||||
QVERIFY(!doc->begin().layout()->formats().isEmpty());
|
||||
delete hl;
|
||||
QVERIFY(doc->begin().layout()->formats().isEmpty());
|
||||
QVERIFY(lout->documentChangedCalled);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::emptyBlocks()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
|
||||
cursor.insertText("Foo");
|
||||
cursor.insertBlock();
|
||||
cursor.insertBlock();
|
||||
hl->highlighted = false;
|
||||
cursor.insertBlock();
|
||||
QVERIFY(hl->highlighted);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::setCharFormat()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
|
||||
cursor.insertText("FooBar");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("Blah");
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
|
||||
QTextCharFormat fmt;
|
||||
fmt.setFontItalic(true);
|
||||
hl->highlighted = false;
|
||||
hl->callCount = 0;
|
||||
cursor.mergeCharFormat(fmt);
|
||||
QVERIFY(hl->highlighted);
|
||||
QCOMPARE(hl->callCount, 2);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::highlightOnInit()
|
||||
{
|
||||
cursor.insertText("Hello");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("World");
|
||||
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
QTRY_VERIFY(hl->highlighted);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::highlightOnInitAndAppend()
|
||||
{
|
||||
cursor.insertText("Hello");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("World");
|
||||
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("More text");
|
||||
QTRY_VERIFY(hl->highlighted);
|
||||
QVERIFY(hl->highlightedText.endsWith(doc->toPlainText().remove(QLatin1Char('\n'))));
|
||||
}
|
||||
|
||||
class StateTestHighlighter : public QSyntaxHighlighter
|
||||
{
|
||||
public:
|
||||
inline StateTestHighlighter(QTextDocument *parent)
|
||||
: QSyntaxHighlighter(parent), state(0), highlighted(false) {}
|
||||
|
||||
inline void reset()
|
||||
{
|
||||
highlighted = false;
|
||||
state = 0;
|
||||
}
|
||||
|
||||
virtual void highlightBlock(const QString &text) override
|
||||
{
|
||||
highlighted = true;
|
||||
if (text == QLatin1String("changestate"))
|
||||
setCurrentBlockState(state++);
|
||||
}
|
||||
|
||||
int state;
|
||||
bool highlighted;
|
||||
};
|
||||
|
||||
void tst_QSyntaxHighlighter::stopHighlightingWhenStateDoesNotChange()
|
||||
{
|
||||
cursor.insertText("state");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("changestate");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("keepstate");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("changestate");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("changestate");
|
||||
|
||||
StateTestHighlighter *hl = new StateTestHighlighter(doc);
|
||||
QTRY_VERIFY(hl->highlighted);
|
||||
|
||||
hl->reset();
|
||||
|
||||
// turn the text of the first block into 'changestate'
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.insertText("change");
|
||||
|
||||
// verify that we highlighted only to the 'keepstate' block,
|
||||
// not beyond
|
||||
QCOMPARE(hl->state, 2);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::unindent()
|
||||
{
|
||||
const QString spaces(" ");
|
||||
const QString text("Foobar");
|
||||
QString plainText;
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
cursor.insertText(spaces + text);
|
||||
cursor.insertBlock();
|
||||
|
||||
plainText += spaces;
|
||||
plainText += text;
|
||||
plainText += QLatin1Char('\n');
|
||||
}
|
||||
QCOMPARE(doc->toPlainText(), plainText);
|
||||
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
QTRY_VERIFY(hl->highlighted);
|
||||
hl->callCount = 0;
|
||||
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.beginEditBlock();
|
||||
|
||||
plainText.clear();
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, 4);
|
||||
cursor.removeSelectedText();
|
||||
cursor.movePosition(QTextCursor::NextBlock);
|
||||
|
||||
plainText += text;
|
||||
plainText += QLatin1Char('\n');
|
||||
}
|
||||
|
||||
cursor.endEditBlock();
|
||||
QCOMPARE(doc->toPlainText(), plainText);
|
||||
QCOMPARE(hl->callCount, 5);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::highlightToEndOfDocument()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
hl->callCount = 0;
|
||||
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.beginEditBlock();
|
||||
|
||||
cursor.insertText("Hello");
|
||||
cursor.insertBlock();
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("World");
|
||||
cursor.insertBlock();
|
||||
|
||||
cursor.endEditBlock();
|
||||
|
||||
QCOMPARE(hl->callCount, 4);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::highlightToEndOfDocument2()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
hl->callCount = 0;
|
||||
|
||||
cursor.movePosition(QTextCursor::End);
|
||||
cursor.beginEditBlock();
|
||||
QTextBlockFormat fmt;
|
||||
fmt.setAlignment(Qt::AlignLeft);
|
||||
cursor.setBlockFormat(fmt);
|
||||
cursor.insertText("Three\nLines\nHere");
|
||||
cursor.endEditBlock();
|
||||
|
||||
QCOMPARE(hl->callCount, 3);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::preservePreeditArea()
|
||||
{
|
||||
QList<QTextLayout::FormatRange> formats;
|
||||
QTextLayout::FormatRange range;
|
||||
range.start = 0;
|
||||
range.length = 8;
|
||||
range.format.setForeground(Qt::blue);
|
||||
formats << range;
|
||||
range.start = 9;
|
||||
range.length = 1;
|
||||
range.format.setForeground(Qt::red);
|
||||
formats << range;
|
||||
TestHighlighter *hl = new TestHighlighter(formats, doc);
|
||||
|
||||
doc->setPlainText("Hello World");
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
|
||||
QTextLayout *layout = cursor.block().layout();
|
||||
|
||||
layout->setPreeditArea(5, QString("foo"));
|
||||
range.start = 5;
|
||||
range.length = 3;
|
||||
range.format.setFontUnderline(true);
|
||||
formats.clear();
|
||||
formats << range;
|
||||
|
||||
hl->callCount = 0;
|
||||
|
||||
cursor.beginEditBlock();
|
||||
layout->setFormats(formats);
|
||||
cursor.endEditBlock();
|
||||
|
||||
QCOMPARE(hl->callCount, 1);
|
||||
|
||||
formats = layout->formats();
|
||||
QCOMPARE(formats.size(), 3);
|
||||
|
||||
range = formats.at(0);
|
||||
|
||||
QCOMPARE(range.start, 5);
|
||||
QCOMPARE(range.length, 3);
|
||||
QVERIFY(range.format.fontUnderline());
|
||||
|
||||
range = formats.at(1);
|
||||
QCOMPARE(range.start, 0);
|
||||
QCOMPARE(range.length, 8 + 3);
|
||||
|
||||
range = formats.at(2);
|
||||
QCOMPARE(range.start, 9 + 3);
|
||||
QCOMPARE(range.length, 1);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::task108530()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
|
||||
cursor.insertText("test");
|
||||
hl->callCount = 0;
|
||||
hl->highlightedText.clear();
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(hl->highlightedText, QString("test"));
|
||||
QCOMPARE(hl->callCount, 2);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::avoidUnnecessaryRehighlight()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
QVERIFY(!hl->highlighted);
|
||||
|
||||
doc->setPlainText("Hello World");
|
||||
QVERIFY(hl->highlighted);
|
||||
|
||||
hl->highlighted = false;
|
||||
QCoreApplication::processEvents();
|
||||
QVERIFY(!hl->highlighted);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::avoidUnnecessaryDelayedRehighlight()
|
||||
{
|
||||
// Having text in the document before creating the highlighter starts the delayed rehighlight
|
||||
cursor.insertText("Hello World");
|
||||
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
QVERIFY(!hl->highlighted);
|
||||
|
||||
hl->rehighlight();
|
||||
QVERIFY(hl->highlighted);
|
||||
|
||||
hl->highlighted = false;
|
||||
// Process events, including delayed rehighlight emission
|
||||
QCoreApplication::processEvents();
|
||||
// Should be cancelled and no extra rehighlight should be done
|
||||
QVERIFY(!hl->highlighted);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::noContentsChangedDuringHighlight()
|
||||
{
|
||||
QList<QTextLayout::FormatRange> formats;
|
||||
QTextLayout::FormatRange range;
|
||||
range.start = 0;
|
||||
range.length = 10;
|
||||
range.format.setForeground(Qt::blue);
|
||||
formats.append(range);
|
||||
|
||||
TestHighlighter *hl = new TestHighlighter(formats, doc);
|
||||
|
||||
lout->documentChangedCalled = false;
|
||||
QTextCursor cursor(doc);
|
||||
|
||||
QSignalSpy contentsChangedSpy(doc, SIGNAL(contentsChanged()));
|
||||
cursor.insertText("Hello World");
|
||||
|
||||
QCOMPARE(contentsChangedSpy.size(), 1);
|
||||
QVERIFY(hl->highlighted);
|
||||
QVERIFY(lout->documentChangedCalled);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::rehighlight()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
hl->callCount = 0;
|
||||
doc->setPlainText("Hello");
|
||||
hl->callCount = 0;
|
||||
hl->rehighlight();
|
||||
QCOMPARE(hl->callCount, 1);
|
||||
}
|
||||
|
||||
void tst_QSyntaxHighlighter::rehighlightBlock()
|
||||
{
|
||||
TestHighlighter *hl = new TestHighlighter(doc);
|
||||
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.beginEditBlock();
|
||||
cursor.insertText("Hello");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("World");
|
||||
cursor.endEditBlock();
|
||||
|
||||
hl->callCount = 0;
|
||||
hl->highlightedText.clear();
|
||||
QTextBlock block = doc->begin();
|
||||
hl->rehighlightBlock(block);
|
||||
|
||||
QCOMPARE(hl->highlightedText, QString("Hello"));
|
||||
QCOMPARE(hl->callCount, 1);
|
||||
|
||||
hl->callCount = 0;
|
||||
hl->highlightedText.clear();
|
||||
hl->rehighlightBlock(block.next());
|
||||
|
||||
QCOMPARE(hl->highlightedText, QString("World"));
|
||||
QCOMPARE(hl->callCount, 1);
|
||||
}
|
||||
|
||||
#ifndef QT_NO_WIDGETS
|
||||
void tst_QSyntaxHighlighter::textEditParent()
|
||||
{
|
||||
QTextEdit textEdit;
|
||||
TestHighlighter *hl = new TestHighlighter(&textEdit);
|
||||
QCOMPARE(hl->document(), textEdit.document());
|
||||
}
|
||||
#endif
|
||||
|
||||
QTEST_MAIN(tst_QSyntaxHighlighter)
|
||||
#include "tst_qsyntaxhighlighter.moc"
|
15
tests/auto/gui/text/qtextblock/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qtextblock/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextblock Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextblock
|
||||
SOURCES
|
||||
tst_qtextblock.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
142
tests/auto/gui/text/qtextblock/tst_qtextblock.cpp
Normal file
142
tests/auto/gui/text/qtextblock/tst_qtextblock.cpp
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QtTest/QTest>
|
||||
|
||||
#include <qtextdocument.h>
|
||||
#include <qdebug.h>
|
||||
#ifndef Q_OS_WIN
|
||||
# include <private/qtextdocument_p.h>
|
||||
#endif
|
||||
|
||||
#include <qtextobject.h>
|
||||
#include <qtextcursor.h>
|
||||
|
||||
QT_FORWARD_DECLARE_CLASS(QTextDocument)
|
||||
|
||||
class tst_QTextBlock : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
void fragmentOverBlockBoundaries();
|
||||
void excludeParagraphSeparatorFragment();
|
||||
void backwardsBlockIterator();
|
||||
void previousBlock_qtbug18026();
|
||||
void removedBlock_qtbug18500();
|
||||
|
||||
private:
|
||||
QTextDocument *doc;
|
||||
QTextCursor cursor;
|
||||
};
|
||||
|
||||
void tst_QTextBlock::init()
|
||||
{
|
||||
doc = new QTextDocument;
|
||||
cursor = QTextCursor(doc);
|
||||
}
|
||||
|
||||
void tst_QTextBlock::cleanup()
|
||||
{
|
||||
cursor = QTextCursor();
|
||||
delete doc;
|
||||
doc = 0;
|
||||
}
|
||||
|
||||
void tst_QTextBlock::fragmentOverBlockBoundaries()
|
||||
{
|
||||
/* this creates two fragments in the piecetable:
|
||||
* 1) 'hello<parag separator here>world'
|
||||
* 2) '<parag separator>'
|
||||
* (they are not united because the former was interested after the latter,
|
||||
* hence their position in the pt buffer is the other way around)
|
||||
*/
|
||||
cursor.insertText("Hello");
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("World");
|
||||
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
|
||||
const QTextDocument *doc = cursor.block().document();
|
||||
QVERIFY(doc);
|
||||
// Block separators are always a fragment of their self. Thus:
|
||||
// |Hello|\b|World|\b|
|
||||
#if !defined(Q_OS_WIN)
|
||||
QCOMPARE(QTextDocumentPrivate::get(doc)->fragmentMap().numNodes(), 4);
|
||||
#endif
|
||||
QCOMPARE(cursor.block().text(), QString("Hello"));
|
||||
cursor.movePosition(QTextCursor::NextBlock);
|
||||
QCOMPARE(cursor.block().text(), QString("World"));
|
||||
}
|
||||
|
||||
void tst_QTextBlock::excludeParagraphSeparatorFragment()
|
||||
{
|
||||
QTextCharFormat fmt;
|
||||
fmt.setForeground(Qt::blue);
|
||||
cursor.insertText("Hello", fmt);
|
||||
|
||||
QTextBlock block = doc->begin();
|
||||
QVERIFY(block.isValid());
|
||||
|
||||
QTextBlock::Iterator it = block.begin();
|
||||
|
||||
QTextFragment fragment = it.fragment();
|
||||
QVERIFY(fragment.isValid());
|
||||
QCOMPARE(fragment.text(), QString("Hello"));
|
||||
|
||||
++it;
|
||||
QVERIFY(it.atEnd());
|
||||
QCOMPARE(it, block.end());
|
||||
}
|
||||
|
||||
void tst_QTextBlock::backwardsBlockIterator()
|
||||
{
|
||||
QTextCharFormat fmt;
|
||||
|
||||
fmt.setForeground(Qt::magenta);
|
||||
cursor.insertText("A", fmt);
|
||||
|
||||
fmt.setForeground(Qt::red);
|
||||
cursor.insertText("A", fmt);
|
||||
|
||||
fmt.setForeground(Qt::magenta);
|
||||
cursor.insertText("A", fmt);
|
||||
|
||||
QTextBlock block = doc->begin();
|
||||
QVERIFY(block.isValid());
|
||||
|
||||
QTextBlock::Iterator it = block.begin();
|
||||
QCOMPARE(it.fragment().position(), 0);
|
||||
++it;
|
||||
QCOMPARE(it.fragment().position(), 1);
|
||||
++it;
|
||||
|
||||
QCOMPARE(it.fragment().position(), 2);
|
||||
|
||||
--it;
|
||||
QCOMPARE(it.fragment().position(), 1);
|
||||
--it;
|
||||
QCOMPARE(it.fragment().position(), 0);
|
||||
}
|
||||
|
||||
void tst_QTextBlock::previousBlock_qtbug18026()
|
||||
{
|
||||
QTextBlock last = doc->end().previous();
|
||||
QVERIFY(last.isValid());
|
||||
}
|
||||
|
||||
void tst_QTextBlock::removedBlock_qtbug18500()
|
||||
{
|
||||
cursor.insertText("line 1\nline 2\nline 3 \nline 4\n");
|
||||
cursor.setPosition(7);
|
||||
QTextBlock block = cursor.block();
|
||||
cursor.setPosition(21, QTextCursor::KeepAnchor);
|
||||
|
||||
cursor.removeSelectedText();
|
||||
QVERIFY(!block.isValid());
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextBlock)
|
||||
#include "tst_qtextblock.moc"
|
15
tests/auto/gui/text/qtextcursor/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qtextcursor/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextcursor Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextcursor
|
||||
SOURCES
|
||||
tst_qtextcursor.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
2185
tests/auto/gui/text/qtextcursor/tst_qtextcursor.cpp
Normal file
2185
tests/auto/gui/text/qtextcursor/tst_qtextcursor.cpp
Normal file
File diff suppressed because it is too large
Load Diff
17
tests/auto/gui/text/qtextdocument/CMakeLists.txt
Normal file
17
tests/auto/gui/text/qtextdocument/CMakeLists.txt
Normal file
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextdocument Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextdocument
|
||||
SOURCES
|
||||
common.h
|
||||
tst_qtextdocument.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
Qt::Xml
|
||||
)
|
55
tests/auto/gui/text/qtextdocument/common.h
Normal file
55
tests/auto/gui/text/qtextdocument/common.h
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
#include <QAbstractTextDocumentLayout>
|
||||
#include <private/qtextdocument_p.h>
|
||||
|
||||
#ifndef COMMON_H
|
||||
#define COMMON_H
|
||||
|
||||
class QTestDocumentLayout : public QAbstractTextDocumentLayout
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QTestDocumentLayout(QTextDocument *doc) : QAbstractTextDocumentLayout(doc), f(-1), called(false) {}
|
||||
virtual void draw(QPainter *, const PaintContext &) override {}
|
||||
virtual int hitTest(const QPointF &, Qt::HitTestAccuracy ) const override { return 0; }
|
||||
|
||||
virtual void documentChanged(int from, int oldLength, int length) override
|
||||
{
|
||||
called = true;
|
||||
lastDocumentLengths.append(QTextDocumentPrivate::get(document())->length());
|
||||
|
||||
if (f < 0)
|
||||
return;
|
||||
|
||||
if(from != f ||
|
||||
o != oldLength ||
|
||||
l != length) {
|
||||
qDebug("checkDocumentChanged: got %d %d %d, expected %d %d %d", from, oldLength, length, f, o, l);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
virtual int pageCount() const override { return 1; }
|
||||
virtual QSizeF documentSize() const override { return QSizeF(); }
|
||||
|
||||
virtual QRectF frameBoundingRect(QTextFrame *) const override { return QRectF(); }
|
||||
virtual QRectF blockBoundingRect(const QTextBlock &) const override { return QRectF(); }
|
||||
|
||||
int f;
|
||||
int o;
|
||||
int l;
|
||||
|
||||
void expect(int from, int oldLength, int length) {
|
||||
f = from;
|
||||
o = oldLength;
|
||||
l = length;
|
||||
error = false;
|
||||
called = false;
|
||||
}
|
||||
bool error;
|
||||
bool called;
|
||||
QList<int> lastDocumentLengths;
|
||||
};
|
||||
|
||||
#endif
|
3
tests/auto/gui/text/qtextdocument/test.css
Normal file
3
tests/auto/gui/text/qtextdocument/test.css
Normal file
@ -0,0 +1,3 @@
|
||||
body {
|
||||
font: normal 400 14px/1.2 Arial;
|
||||
}
|
3969
tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
Normal file
3969
tests/auto/gui/text/qtextdocument/tst_qtextdocument.cpp
Normal file
File diff suppressed because it is too large
Load Diff
15
tests/auto/gui/text/qtextdocumentfragment/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qtextdocumentfragment/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextdocumentfragment Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextdocumentfragment
|
||||
SOURCES
|
||||
tst_qtextdocumentfragment.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
File diff suppressed because it is too large
Load Diff
6
tests/auto/gui/text/qtextdocumentlayout/BLACKLIST
Normal file
6
tests/auto/gui/text/qtextdocumentlayout/BLACKLIST
Normal file
@ -0,0 +1,6 @@
|
||||
[imageAtRightAlignedTab]
|
||||
rhel-6.6
|
||||
rhel-7.4
|
||||
rhel-7.6
|
||||
sles
|
||||
centos
|
21
tests/auto/gui/text/qtextdocumentlayout/CMakeLists.txt
Normal file
21
tests/auto/gui/text/qtextdocumentlayout/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextdocumentlayout Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextdocumentlayout
|
||||
SOURCES
|
||||
tst_qtextdocumentlayout.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qtextdocumentlayout CONDITION TARGET Qt::Widgets
|
||||
LIBRARIES
|
||||
Qt::Widgets
|
||||
)
|
@ -0,0 +1,421 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <qtextdocument.h>
|
||||
#include <qabstracttextdocumentlayout.h>
|
||||
#include <qdebug.h>
|
||||
#include <qpainter.h>
|
||||
#include <qtexttable.h>
|
||||
#ifndef QT_NO_WIDGETS
|
||||
#include <qtextedit.h>
|
||||
#include <qscrollbar.h>
|
||||
#endif
|
||||
|
||||
class tst_QTextDocumentLayout : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
void cleanupTestCase();
|
||||
void defaultPageSizeHandling();
|
||||
void idealWidth();
|
||||
void lineSeparatorFollowingTable();
|
||||
#ifndef QT_NO_WIDGETS
|
||||
void wrapAtWordBoundaryOrAnywhere();
|
||||
#endif
|
||||
void inlineImage();
|
||||
void clippedTableCell();
|
||||
void floatingTablePageBreak();
|
||||
void imageAtRightAlignedTab();
|
||||
void blockVisibility();
|
||||
void testHitTest();
|
||||
|
||||
void largeImage();
|
||||
|
||||
private:
|
||||
QTextDocument *doc;
|
||||
};
|
||||
|
||||
void tst_QTextDocumentLayout::init()
|
||||
{
|
||||
doc = new QTextDocument;
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::cleanup()
|
||||
{
|
||||
delete doc;
|
||||
doc = 0;
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::cleanupTestCase()
|
||||
{
|
||||
if (qgetenv("QTEST_KEEP_IMAGEDATA").toInt() == 0) {
|
||||
QFile::remove(QLatin1String("expected.png"));
|
||||
QFile::remove(QLatin1String("img.png"));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::defaultPageSizeHandling()
|
||||
{
|
||||
QAbstractTextDocumentLayout *layout = doc->documentLayout();
|
||||
QVERIFY(layout);
|
||||
|
||||
QVERIFY(!doc->pageSize().isValid());
|
||||
QSizeF docSize = layout->documentSize();
|
||||
QVERIFY(docSize.width() > 0 && docSize.width() < 1000);
|
||||
QVERIFY(docSize.height() > 0 && docSize.height() < 1000);
|
||||
|
||||
doc->setPlainText("Some text\nwith a few lines\nand not real information\nor anything otherwise useful");
|
||||
|
||||
docSize = layout->documentSize();
|
||||
QVERIFY(docSize.isValid());
|
||||
QVERIFY(docSize.width() != INT_MAX);
|
||||
QVERIFY(docSize.height() != INT_MAX);
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::idealWidth()
|
||||
{
|
||||
doc->setPlainText("Some text\nwith a few lines\nand not real information\nor anything otherwise useful");
|
||||
doc->setTextWidth(1000);
|
||||
QCOMPARE(doc->textWidth(), qreal(1000));
|
||||
QCOMPARE(doc->size().width(), doc->textWidth());
|
||||
QVERIFY(doc->idealWidth() < doc->textWidth());
|
||||
QVERIFY(doc->idealWidth() > 0);
|
||||
|
||||
QTextBlockFormat fmt;
|
||||
fmt.setAlignment(Qt::AlignRight | Qt::AlignAbsolute);
|
||||
QTextCursor cursor(doc);
|
||||
cursor.select(QTextCursor::Document);
|
||||
cursor.mergeBlockFormat(fmt);
|
||||
|
||||
QCOMPARE(doc->textWidth(), qreal(1000));
|
||||
QCOMPARE(doc->size().width(), doc->textWidth());
|
||||
QVERIFY(doc->idealWidth() < doc->textWidth());
|
||||
QVERIFY(doc->idealWidth() > 0);
|
||||
}
|
||||
|
||||
// none of the QTextLine items in the document should intersect with the margin rect
|
||||
void tst_QTextDocumentLayout::lineSeparatorFollowingTable()
|
||||
{
|
||||
QString html_begin("<html><table border=1><tr><th>Column 1</th></tr><tr><td>Data</td></tr></table><br>");
|
||||
QString html_text("bla bla bla bla bla bla bla bla<br>");
|
||||
QString html_end("<table border=1><tr><th>Column 1</th></tr><tr><td>Data</td></tr></table></html>");
|
||||
|
||||
QString html = html_begin;
|
||||
|
||||
for (int i = 0; i < 80; ++i)
|
||||
html += html_text;
|
||||
|
||||
html += html_end;
|
||||
|
||||
doc->setHtml(html);
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
|
||||
const int margin = 87;
|
||||
const int pageWidth = 873;
|
||||
const int pageHeight = 1358;
|
||||
|
||||
QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
|
||||
fmt.setMargin(margin);
|
||||
doc->rootFrame()->setFrameFormat(fmt);
|
||||
|
||||
QFont font(doc->defaultFont());
|
||||
font.setPointSize(10);
|
||||
doc->setDefaultFont(font);
|
||||
doc->setPageSize(QSizeF(pageWidth, pageHeight));
|
||||
|
||||
QRectF marginRect(QPointF(0, pageHeight - margin), QSizeF(pageWidth, 2 * margin));
|
||||
|
||||
// force layouting
|
||||
doc->pageCount();
|
||||
|
||||
for (QTextBlock block = doc->begin(); block != doc->end(); block = block.next()) {
|
||||
QTextLayout *layout = block.layout();
|
||||
for (int i = 0; i < layout->lineCount(); ++i) {
|
||||
QTextLine line = layout->lineAt(i);
|
||||
QRectF rect = line.rect().translated(layout->position());
|
||||
QVERIFY(!rect.intersects(marginRect));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef QT_NO_WIDGETS
|
||||
void tst_QTextDocumentLayout::wrapAtWordBoundaryOrAnywhere()
|
||||
{
|
||||
//task 150562
|
||||
QTextEdit edit;
|
||||
edit.setText("<table><tr><td>hello hello hello"
|
||||
"thisisabigwordthisisabigwordthisisabigwordthisisabigwordthisisabigword"
|
||||
"hello hello hello</td></tr></table>");
|
||||
edit.setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||
edit.resize(100, 100);
|
||||
edit.show();
|
||||
QVERIFY(!edit.horizontalScrollBar()->isVisible());
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_QTextDocumentLayout::inlineImage()
|
||||
{
|
||||
doc->setPageSize(QSizeF(800, 500));
|
||||
|
||||
QImage img(400, 400, QImage::Format_RGB32);
|
||||
QLatin1String name("bigImage");
|
||||
|
||||
doc->addResource(QTextDocument::ImageResource, QUrl(name), img);
|
||||
|
||||
QTextImageFormat imgFormat;
|
||||
imgFormat.setName(name);
|
||||
imgFormat.setWidth(img.width());
|
||||
|
||||
QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
|
||||
qreal height = doc->pageSize().height() - fmt.topMargin() - fmt.bottomMargin();
|
||||
imgFormat.setHeight(height);
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
cursor.insertImage(imgFormat);
|
||||
|
||||
QCOMPARE(doc->pageCount(), 1);
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::clippedTableCell()
|
||||
{
|
||||
const char *html =
|
||||
"<table style=\" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\""
|
||||
"border=\"0\" margin=\"0\" cellpadding=\"0\" cellspacing=\"0\"><tr><td></td></tr></table>";
|
||||
|
||||
doc->setHtml(html);
|
||||
doc->pageSize();
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
cursor.movePosition(QTextCursor::Right);
|
||||
|
||||
QTextTable *table = cursor.currentTable();
|
||||
QVERIFY(table);
|
||||
|
||||
QTextCursor cellCursor = table->cellAt(0, 0).firstCursorPosition();
|
||||
QImage src(16, 16, QImage::Format_ARGB32_Premultiplied);
|
||||
src.fill(0xffff0000);
|
||||
cellCursor.insertImage(src);
|
||||
|
||||
QTextBlock block = cellCursor.block();
|
||||
QRectF r = doc->documentLayout()->blockBoundingRect(block);
|
||||
|
||||
QRectF rect(0, 0, r.left() + 1, 64);
|
||||
|
||||
QImage img(64, 64, QImage::Format_ARGB32_Premultiplied);
|
||||
img.fill(0x0);
|
||||
QImage expected = img;
|
||||
QPainter p(&img);
|
||||
doc->drawContents(&p, rect);
|
||||
p.end();
|
||||
p.begin(&expected);
|
||||
r.setWidth(1);
|
||||
p.fillRect(r, Qt::red);
|
||||
p.end();
|
||||
|
||||
img.save("img.png");
|
||||
expected.save("expected.png");
|
||||
QCOMPARE(img, expected);
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::floatingTablePageBreak()
|
||||
{
|
||||
doc->clear();
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
|
||||
QTextTableFormat tableFormat;
|
||||
tableFormat.setPosition(QTextFrameFormat::FloatLeft);
|
||||
QTextTable *table = cursor.insertTable(50, 1, tableFormat);
|
||||
Q_UNUSED(table);
|
||||
|
||||
// Make height of document 2/3 of the table, fitting the table into two pages
|
||||
QSizeF documentSize = doc->size();
|
||||
documentSize.rheight() *= 2.0 / 3.0;
|
||||
|
||||
doc->setPageSize(documentSize);
|
||||
|
||||
QCOMPARE(doc->pageCount(), 2);
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::imageAtRightAlignedTab()
|
||||
{
|
||||
doc->clear();
|
||||
|
||||
QTextFrameFormat fmt = doc->rootFrame()->frameFormat();
|
||||
fmt.setMargin(0);
|
||||
doc->rootFrame()->setFrameFormat(fmt);
|
||||
|
||||
QTextCursor cursor(doc);
|
||||
QTextBlockFormat blockFormat;
|
||||
QList<QTextOption::Tab> tabs;
|
||||
QTextOption::Tab tab;
|
||||
tab.position = 300;
|
||||
tab.type = QTextOption::RightTab;
|
||||
tabs.append(tab);
|
||||
blockFormat.setTabPositions(tabs);
|
||||
|
||||
// First block: text, some of it right-aligned
|
||||
cursor.insertBlock(blockFormat);
|
||||
cursor.insertText("first line\t");
|
||||
cursor.insertText("right-aligned text");
|
||||
|
||||
// Second block: text, then right-aligned image
|
||||
cursor.insertBlock(blockFormat);
|
||||
cursor.insertText("second line\t");
|
||||
QImage img(48, 48, QImage::Format_RGB32);
|
||||
const QString name = QString::fromLatin1("image");
|
||||
doc->addResource(QTextDocument::ImageResource, QUrl(name), img);
|
||||
QTextImageFormat imgFormat;
|
||||
imgFormat.setName(name);
|
||||
cursor.insertImage(imgFormat);
|
||||
|
||||
// Everything should fit into the 300 pixels
|
||||
qreal bearing = QFontMetricsF(doc->defaultFont()).rightBearing(QLatin1Char('t'));
|
||||
QCOMPARE(doc->idealWidth(), std::max(300.0, 300.0 - bearing));
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::blockVisibility()
|
||||
{
|
||||
QTextCursor cursor(doc);
|
||||
for (int i = 0; i < 10; ++i) {
|
||||
if (!doc->isEmpty())
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("A");
|
||||
}
|
||||
|
||||
qreal margin = doc->documentMargin();
|
||||
QSizeF emptySize(2 * margin, 2 * margin);
|
||||
QSizeF halfSize = doc->size();
|
||||
halfSize.rheight() -= 2 * margin;
|
||||
halfSize.rheight() /= 2;
|
||||
halfSize.rheight() += 2 * margin;
|
||||
|
||||
for (int i = 0; i < 10; i += 2) {
|
||||
QTextBlock block = doc->findBlockByNumber(i);
|
||||
block.setVisible(false);
|
||||
doc->markContentsDirty(block.position(), block.length());
|
||||
}
|
||||
|
||||
QCOMPARE(doc->size(), halfSize);
|
||||
|
||||
for (int i = 1; i < 10; i += 2) {
|
||||
QTextBlock block = doc->findBlockByNumber(i);
|
||||
block.setVisible(false);
|
||||
doc->markContentsDirty(block.position(), block.length());
|
||||
}
|
||||
|
||||
QCOMPARE(doc->size(), emptySize);
|
||||
|
||||
for (int i = 0; i < 10; i += 2) {
|
||||
QTextBlock block = doc->findBlockByNumber(i);
|
||||
block.setVisible(true);
|
||||
doc->markContentsDirty(block.position(), block.length());
|
||||
}
|
||||
|
||||
QCOMPARE(doc->size(), halfSize);
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::largeImage()
|
||||
{
|
||||
auto img = QImage(400, 500, QImage::Format_ARGB32_Premultiplied);
|
||||
img.fill(Qt::black);
|
||||
|
||||
{
|
||||
QTextDocument document;
|
||||
|
||||
document.addResource(QTextDocument::ImageResource,
|
||||
QUrl("data://test.png"), QVariant(img));
|
||||
document.setPageSize({500, 504});
|
||||
|
||||
auto html = "<img src=\"data://test.png\">";
|
||||
document.setHtml(html);
|
||||
|
||||
QCOMPARE(document.pageCount(), 2);
|
||||
}
|
||||
|
||||
{
|
||||
QTextDocument document;
|
||||
|
||||
document.addResource(QTextDocument::ImageResource,
|
||||
QUrl("data://test.png"), QVariant(img));
|
||||
document.setPageSize({500, 508});
|
||||
|
||||
auto html = "<img src=\"data://test.png\">";
|
||||
document.setHtml(html);
|
||||
|
||||
QCOMPARE(document.pageCount(), 1);
|
||||
}
|
||||
|
||||
{
|
||||
QTextDocument document;
|
||||
|
||||
document.addResource(QTextDocument::ImageResource,
|
||||
QUrl("data://test.png"), QVariant(img));
|
||||
document.setPageSize({585, 250});
|
||||
|
||||
auto html = "<img src=\"data://test.png\">";
|
||||
document.setHtml(html);
|
||||
|
||||
QCOMPARE(document.pageCount(), 3);
|
||||
}
|
||||
|
||||
{
|
||||
QTextDocument document;
|
||||
|
||||
document.addResource(QTextDocument::ImageResource,
|
||||
QUrl("data://test.png"), QVariant(img));
|
||||
document.setPageSize({585, 258});
|
||||
|
||||
auto html = "<img src=\"data://test.png\">";
|
||||
document.setHtml(html);
|
||||
|
||||
QCOMPARE(document.pageCount(), 2);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextDocumentLayout::testHitTest()
|
||||
{
|
||||
QTextDocument document;
|
||||
QTextCursor cur(&document);
|
||||
int topMargin = 20;
|
||||
|
||||
//insert 500 blocks into textedit
|
||||
for (int i = 0; i < 500; i++) {
|
||||
cur.insertBlock();
|
||||
cur.insertHtml(QString("block %1").arg(i));
|
||||
}
|
||||
|
||||
//randomly set half the blocks invisible
|
||||
QTextBlock blk=document.begin();
|
||||
for (int i = 0; i < 500; i++) {
|
||||
if (i % 7)
|
||||
blk.setVisible(0);
|
||||
blk = blk.next();
|
||||
}
|
||||
|
||||
//set margin for all blocks (not strictly necessary, but makes easier to click in between blocks)
|
||||
QTextBlockFormat blkfmt;
|
||||
blkfmt.setTopMargin(topMargin);
|
||||
cur.movePosition(QTextCursor::Start);
|
||||
cur.movePosition(QTextCursor::End, QTextCursor::KeepAnchor);
|
||||
cur.mergeBlockFormat(blkfmt);
|
||||
|
||||
for (int y = cur.selectionStart(); y < cur.selectionEnd(); y += 10) {
|
||||
QPoint mousePoint(1, y);
|
||||
int cursorPos = document.documentLayout()->hitTest(mousePoint, Qt::FuzzyHit);
|
||||
int positionY = document.findBlock(cursorPos).layout()->position().toPoint().y();
|
||||
//mousePoint is in the rect of the current Block
|
||||
QVERIFY(positionY - topMargin <= y);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextDocumentLayout)
|
||||
#include "tst_qtextdocumentlayout.moc"
|
15
tests/auto/gui/text/qtextformat/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qtextformat/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextformat Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextformat
|
||||
SOURCES
|
||||
tst_qtextformat.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
796
tests/auto/gui/text/qtextformat/tst_qtextformat.cpp
Normal file
796
tests/auto/gui/text/qtextformat/tst_qtextformat.cpp
Normal file
@ -0,0 +1,796 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
#include <QBuffer>
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qdebug.h>
|
||||
#include <qsettings.h>
|
||||
#include <qtextformat.h>
|
||||
#include <private/qtextformat_p.h>
|
||||
#include <qtextdocument.h>
|
||||
#include <qtextcursor.h>
|
||||
#include <qtextobject.h>
|
||||
#include <qtextlayout.h>
|
||||
#include <qabstracttextdocumentlayout.h>
|
||||
|
||||
#ifndef QT_NO_DATASTREAM
|
||||
# include <qdatastream.h>
|
||||
#endif
|
||||
|
||||
class tst_QTextFormat : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void getSetCheck();
|
||||
void defaultAlignment();
|
||||
void testQTextCharFormat() const;
|
||||
void testUnderlinePropertyPrecedence();
|
||||
void toFormat();
|
||||
void resolveFont();
|
||||
void testLetterSpacing();
|
||||
void testFontStretch();
|
||||
void getSetTabs();
|
||||
void testTabsUsed();
|
||||
void testFontStyleSetters();
|
||||
void setFont_data();
|
||||
void setFont();
|
||||
void setFont_collection_data();
|
||||
void setFont_collection();
|
||||
void clearCollection();
|
||||
|
||||
#ifndef QT_NO_DATASTREAM
|
||||
void dataStreamCompatibility();
|
||||
#endif
|
||||
};
|
||||
|
||||
/*! \internal
|
||||
This (used to) trigger a crash in:
|
||||
|
||||
QDataStream &operator>>(QDataStream &stream, QTextFormat &fmt)
|
||||
|
||||
which is most easily produced through QSettings.
|
||||
*/
|
||||
void tst_QTextFormat::testQTextCharFormat() const
|
||||
{
|
||||
QSettings settings("test", "test");
|
||||
QTextCharFormat test;
|
||||
|
||||
settings.value("test", test);
|
||||
}
|
||||
|
||||
// Testing get/set functions
|
||||
void tst_QTextFormat::getSetCheck()
|
||||
{
|
||||
QTextFormat obj1;
|
||||
// int QTextFormat::objectIndex()
|
||||
// void QTextFormat::setObjectIndex(int)
|
||||
obj1.setObjectIndex(0);
|
||||
QCOMPARE(0, obj1.objectIndex());
|
||||
obj1.setObjectIndex(INT_MIN);
|
||||
QCOMPARE(INT_MIN, obj1.objectIndex());
|
||||
obj1.setObjectIndex(INT_MAX);
|
||||
QCOMPARE(INT_MAX, obj1.objectIndex());
|
||||
}
|
||||
|
||||
void tst_QTextFormat::defaultAlignment()
|
||||
{
|
||||
QTextBlockFormat fmt;
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::BlockAlignment));
|
||||
QCOMPARE(fmt.intProperty(QTextFormat::BlockAlignment), 0);
|
||||
QCOMPARE(fmt.alignment(), Qt::AlignLeft);
|
||||
}
|
||||
|
||||
void tst_QTextFormat::testUnderlinePropertyPrecedence()
|
||||
{
|
||||
QTextCharFormat format;
|
||||
// use normal accessors and check internal state
|
||||
format.setUnderlineStyle(QTextCharFormat::NoUnderline);
|
||||
QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
|
||||
QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), false);
|
||||
QCOMPARE(format.property(QTextFormat::FontUnderline).toBool(), false);
|
||||
QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).toInt(), 0);
|
||||
|
||||
format = QTextCharFormat();
|
||||
format.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||||
QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
|
||||
QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), false);
|
||||
QCOMPARE(format.property(QTextFormat::FontUnderline).toBool(), true);
|
||||
QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).toInt(), 1);
|
||||
|
||||
format = QTextCharFormat();
|
||||
format.setUnderlineStyle(QTextCharFormat::DotLine);
|
||||
QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
|
||||
QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), false);
|
||||
QCOMPARE(format.property(QTextFormat::FontUnderline).toBool(), false);
|
||||
QVERIFY(format.property(QTextFormat::TextUnderlineStyle).toInt() > 0);
|
||||
|
||||
// override accessors and use setProperty to create a false state.
|
||||
// then check font()
|
||||
format = QTextCharFormat();
|
||||
format.setProperty(QTextCharFormat::FontUnderline, true);
|
||||
QCOMPARE(format.property(QTextFormat::FontUnderline).isNull(), false);
|
||||
QCOMPARE(format.property(QTextFormat::TextUnderlineStyle).isNull(), true);
|
||||
QCOMPARE(format.fontUnderline(), true);
|
||||
QCOMPARE(format.font().underline(), true);
|
||||
|
||||
format = QTextCharFormat();
|
||||
// create conflict. Should use the new property
|
||||
format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::SingleUnderline);
|
||||
format.setProperty(QTextCharFormat::FontUnderline, false);
|
||||
QCOMPARE(format.fontUnderline(), true);
|
||||
QCOMPARE(format.font().underline(), true);
|
||||
|
||||
format = QTextCharFormat();
|
||||
// create conflict. Should use the new property
|
||||
format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::NoUnderline);
|
||||
format.setProperty(QTextCharFormat::FontUnderline, true);
|
||||
QCOMPARE(format.fontUnderline(), false);
|
||||
QCOMPARE(format.font().underline(), false);
|
||||
|
||||
// do it again, but reverse the ordering (we use a QList internally, so test a LOT ;)
|
||||
// create conflict. Should use the new property
|
||||
format.setProperty(QTextCharFormat::FontUnderline, false);
|
||||
format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::SingleUnderline);
|
||||
QCOMPARE(format.fontUnderline(), true);
|
||||
QCOMPARE(format.font().underline(), true);
|
||||
|
||||
format = QTextCharFormat();
|
||||
// create conflict. Should use the new property
|
||||
format.setProperty(QTextCharFormat::FontUnderline, true);
|
||||
format.setProperty(QTextCharFormat::TextUnderlineStyle, QTextCharFormat::NoUnderline);
|
||||
QCOMPARE(format.fontUnderline(), false);
|
||||
QCOMPARE(format.font().underline(), false);
|
||||
}
|
||||
|
||||
void tst_QTextFormat::toFormat()
|
||||
{
|
||||
{
|
||||
QTextFormat fmt = QTextFrameFormat();
|
||||
QCOMPARE(fmt.toFrameFormat().type(), int(QTextFormat::FrameFormat));
|
||||
}
|
||||
|
||||
{
|
||||
QTextFormat fmt = QTextTableFormat();
|
||||
QCOMPARE(fmt.toTableFormat().type(), int(QTextFormat::FrameFormat));
|
||||
QCOMPARE(fmt.toTableFormat().objectType(), int(QTextFormat::TableObject));
|
||||
}
|
||||
|
||||
{
|
||||
QTextFormat fmt = QTextBlockFormat();
|
||||
QCOMPARE(fmt.toBlockFormat().type(), int(QTextFormat::BlockFormat));
|
||||
}
|
||||
|
||||
{
|
||||
QTextFormat fmt = QTextCharFormat();
|
||||
QCOMPARE(fmt.toCharFormat().type(), int(QTextFormat::CharFormat));
|
||||
}
|
||||
|
||||
{
|
||||
QTextFormat fmt = QTextListFormat();
|
||||
QCOMPARE(fmt.toListFormat().type(), int(QTextFormat::ListFormat));
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextFormat::resolveFont()
|
||||
{
|
||||
QTextDocument doc;
|
||||
|
||||
QTextCharFormat fmt;
|
||||
fmt.setProperty(QTextFormat::ForegroundBrush, QColor(Qt::blue));
|
||||
QCOMPARE(fmt.property(QTextFormat::ForegroundBrush).userType(), qMetaTypeId<QColor>());
|
||||
QCOMPARE(fmt.property(QTextFormat::ForegroundBrush).value<QColor>(), QColor(Qt::blue));
|
||||
fmt.setProperty(QTextFormat::FontItalic, true);
|
||||
QTextCursor(&doc).insertText("Test", fmt);
|
||||
|
||||
QList<QTextFormat> formats = doc.allFormats();
|
||||
QCOMPARE(formats.size(), 3);
|
||||
|
||||
QCOMPARE(formats.at(2).type(), int(QTextFormat::CharFormat));
|
||||
fmt = formats.at(2).toCharFormat();
|
||||
|
||||
QVERIFY(!fmt.font().underline());
|
||||
QVERIFY(fmt.hasProperty(QTextFormat::ForegroundBrush));
|
||||
|
||||
QFont f;
|
||||
f.setUnderline(true);
|
||||
doc.setDefaultFont(f);
|
||||
formats = doc.allFormats();
|
||||
fmt = formats.at(2).toCharFormat();
|
||||
|
||||
QVERIFY(fmt.font().underline());
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
|
||||
|
||||
// verify that deleting a non-existent property does not affect the font resolving
|
||||
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::BackgroundBrush));
|
||||
fmt.clearProperty(QTextFormat::BackgroundBrush);
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::BackgroundBrush));
|
||||
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
|
||||
QVERIFY(fmt.font().underline());
|
||||
|
||||
// verify that deleting an existent but font _unrelated_ property does not affect the font resolving
|
||||
|
||||
QVERIFY(fmt.hasProperty(QTextFormat::ForegroundBrush));
|
||||
fmt.clearProperty(QTextFormat::ForegroundBrush);
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::ForegroundBrush));
|
||||
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
|
||||
QVERIFY(fmt.font().underline());
|
||||
|
||||
// verify that removing a font property _does_ clear the resolving
|
||||
|
||||
QVERIFY(fmt.hasProperty(QTextFormat::FontItalic));
|
||||
fmt.clearProperty(QTextFormat::FontItalic);
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontItalic));
|
||||
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
|
||||
QVERIFY(!fmt.font().underline());
|
||||
QVERIFY(!fmt.font().italic());
|
||||
|
||||
// reset
|
||||
fmt = formats.at(2).toCharFormat();
|
||||
|
||||
QVERIFY(fmt.font().underline());
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
|
||||
|
||||
// verify that _setting_ an unrelated property does _not_ affect the resolving
|
||||
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::IsAnchor));
|
||||
fmt.setProperty(QTextFormat::IsAnchor, true);
|
||||
QVERIFY(fmt.hasProperty(QTextFormat::IsAnchor));
|
||||
|
||||
QVERIFY(fmt.font().underline());
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
|
||||
|
||||
// verify that setting a _related_ font property does affect the resolving
|
||||
//
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontStrikeOut));
|
||||
fmt.setProperty(QTextFormat::FontStrikeOut, true);
|
||||
QVERIFY(fmt.hasProperty(QTextFormat::FontStrikeOut));
|
||||
|
||||
QVERIFY(!fmt.font().underline());
|
||||
QVERIFY(!fmt.hasProperty(QTextFormat::FontUnderline));
|
||||
QVERIFY(fmt.font().strikeOut());
|
||||
}
|
||||
|
||||
|
||||
void tst_QTextFormat::testLetterSpacing()
|
||||
{
|
||||
QTextCharFormat format;
|
||||
|
||||
QCOMPARE(format.hasProperty(QTextFormat::FontLetterSpacing), false);
|
||||
QCOMPARE(format.hasProperty(QTextFormat::FontLetterSpacingType), false);
|
||||
|
||||
format.setFontLetterSpacingType(QFont::AbsoluteSpacing);
|
||||
QCOMPARE(format.font().letterSpacingType(), QFont::AbsoluteSpacing);
|
||||
|
||||
format.setFontLetterSpacing(10.0);
|
||||
QCOMPARE(format.font().letterSpacing(), 10.0);
|
||||
|
||||
QCOMPARE(format.hasProperty(QTextFormat::FontLetterSpacing), true);
|
||||
QCOMPARE(format.property(QTextFormat::FontLetterSpacing).toDouble(), 10.0);
|
||||
QCOMPARE(format.property(QTextFormat::FontLetterSpacingType).toInt(), int(QFont::AbsoluteSpacing));
|
||||
|
||||
format.setFontLetterSpacingType(QFont::PercentageSpacing);
|
||||
QCOMPARE(format.font().letterSpacingType(), QFont::PercentageSpacing);
|
||||
|
||||
format.setFontLetterSpacing(110.0);
|
||||
QCOMPARE(format.font().letterSpacing(), 110.0);
|
||||
|
||||
QCOMPARE(format.property(QTextFormat::FontLetterSpacing).toDouble(), 110.0);
|
||||
QCOMPARE(format.property(QTextFormat::FontLetterSpacingType).toInt(), int(QFont::PercentageSpacing));
|
||||
|
||||
format.setFontLetterSpacingType(QFont::AbsoluteSpacing);
|
||||
QCOMPARE(format.font().letterSpacingType(), QFont::AbsoluteSpacing);
|
||||
|
||||
format.setFontLetterSpacing(10.0);
|
||||
QCOMPARE(format.font().letterSpacing(), 10.0);
|
||||
|
||||
QCOMPARE(format.property(QTextFormat::FontLetterSpacingType).toInt(), int(QFont::AbsoluteSpacing));
|
||||
QCOMPARE(format.property(QTextFormat::FontLetterSpacing).toDouble(), 10.0);
|
||||
}
|
||||
|
||||
void tst_QTextFormat::testFontStretch()
|
||||
{
|
||||
QTextCharFormat format;
|
||||
|
||||
QCOMPARE(format.hasProperty(QTextFormat::FontStretch), false);
|
||||
|
||||
format.setFontStretch(130.0);
|
||||
|
||||
QCOMPARE(format.property(QTextFormat::FontStretch).toInt(), 130);
|
||||
}
|
||||
|
||||
void tst_QTextFormat::getSetTabs()
|
||||
{
|
||||
class Comparator {
|
||||
public:
|
||||
Comparator(const QList<QTextOption::Tab> &tabs, const QList<QTextOption::Tab> &tabs2)
|
||||
{
|
||||
QCOMPARE(tabs.size(), tabs2.size());
|
||||
for(int i=0; i < tabs.size(); i++) {
|
||||
QTextOption::Tab t1 = tabs[i];
|
||||
QTextOption::Tab t2 = tabs2[i];
|
||||
QCOMPARE(t1.position, t2.position);
|
||||
QCOMPARE(t1.type, t2.type);
|
||||
QCOMPARE(t1.delimiter, t2.delimiter);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
QList<QTextOption::Tab> tabs;
|
||||
QTextBlockFormat format;
|
||||
format.setTabPositions(tabs);
|
||||
Comparator c1(tabs, format.tabPositions());
|
||||
|
||||
QTextOption::Tab tab1;
|
||||
tab1.position = 1234;
|
||||
tabs.append(tab1);
|
||||
format.setTabPositions(tabs);
|
||||
Comparator c2(tabs, format.tabPositions());
|
||||
|
||||
QTextOption::Tab tab2(3456, QTextOption::RightTab, QChar('x'));
|
||||
tabs.append(tab2);
|
||||
format.setTabPositions(tabs);
|
||||
Comparator c3(tabs, format.tabPositions());
|
||||
}
|
||||
|
||||
void tst_QTextFormat::testTabsUsed()
|
||||
{
|
||||
QTextDocument doc;
|
||||
QTextCursor cursor(&doc);
|
||||
|
||||
QList<QTextOption::Tab> tabs;
|
||||
QTextBlockFormat format;
|
||||
QTextOption::Tab tab;
|
||||
tab.position = 100;
|
||||
tabs.append(tab);
|
||||
format.setTabPositions(tabs);
|
||||
cursor.mergeBlockFormat(format);
|
||||
cursor.insertText("foo\tbar");
|
||||
//doc.setPageSize(QSizeF(200, 200));
|
||||
doc.documentLayout()->pageCount(); // force layout;
|
||||
|
||||
QTextBlock block = doc.begin();
|
||||
QTextLayout *layout = block.layout();
|
||||
QVERIFY(layout);
|
||||
QCOMPARE(layout->lineCount(), 1);
|
||||
QTextLine line = layout->lineAt(0);
|
||||
QCOMPARE(line.cursorToX(4), 100.);
|
||||
|
||||
QTextOption option = layout->textOption();
|
||||
QCOMPARE(option.tabs().size(), tabs.size());
|
||||
|
||||
}
|
||||
|
||||
void tst_QTextFormat::testFontStyleSetters()
|
||||
{
|
||||
QTextCharFormat format;
|
||||
|
||||
// test the setters
|
||||
format.setFontStyleHint(QFont::Serif);
|
||||
QCOMPARE(format.font().styleHint(), QFont::Serif);
|
||||
QCOMPARE(format.font().styleStrategy(), QFont::PreferDefault);
|
||||
format.setFontStyleStrategy(QFont::PreferOutline);
|
||||
QCOMPARE(format.font().styleStrategy(), QFont::PreferOutline);
|
||||
|
||||
// test setting properties through setFont()
|
||||
QFont font;
|
||||
font.setStyleHint(QFont::SansSerif, QFont::PreferAntialias);
|
||||
format.setFont(font);
|
||||
QCOMPARE(format.font().styleHint(), QFont::SansSerif);
|
||||
QCOMPARE(format.font().styleStrategy(), QFont::PreferAntialias);
|
||||
|
||||
// test kerning
|
||||
format.setFontKerning(false);
|
||||
QCOMPARE(format.font().kerning(), false);
|
||||
format.setFontKerning(true);
|
||||
QCOMPARE(format.font().kerning(), true);
|
||||
font.setKerning(false);
|
||||
format.setFont(font);
|
||||
QCOMPARE(format.font().kerning(), false);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(QTextCharFormat)
|
||||
|
||||
void tst_QTextFormat::setFont_data()
|
||||
{
|
||||
QTest::addColumn<QTextCharFormat>("format1");
|
||||
QTest::addColumn<QTextCharFormat>("format2");
|
||||
QTest::addColumn<bool>("overrideAll");
|
||||
|
||||
QTextCharFormat format1;
|
||||
format1.setFontStyleHint(QFont::Serif);
|
||||
format1.setFontStyleStrategy(QFont::PreferOutline);
|
||||
format1.setFontCapitalization(QFont::AllUppercase);
|
||||
format1.setFontKerning(true);
|
||||
|
||||
{
|
||||
QTest::newRow("noop|override") << format1 << format1 << true;
|
||||
QTest::newRow("noop|inherit") << format1 << format1 << false;
|
||||
}
|
||||
|
||||
{
|
||||
QTextCharFormat format2;
|
||||
format2.setFontStyleHint(QFont::SansSerif);
|
||||
format2.setFontStyleStrategy(QFont::PreferAntialias);
|
||||
format2.setFontCapitalization(QFont::MixedCase);
|
||||
format2.setFontKerning(false);
|
||||
|
||||
QTest::newRow("coverage|override") << format1 << format2 << true;
|
||||
QTest::newRow("coverage|inherit") << format1 << format2 << false;
|
||||
}
|
||||
|
||||
{
|
||||
QTextCharFormat format2;
|
||||
format2.setFontStyleHint(QFont::SansSerif);
|
||||
format2.setFontStyleStrategy(QFont::PreferAntialias);
|
||||
|
||||
QTest::newRow("partial|override") << format1 << format2 << true;
|
||||
QTest::newRow("partial|inherit") << format1 << format2 << false;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextFormat::setFont()
|
||||
{
|
||||
QFETCH(QTextCharFormat, format1);
|
||||
QFETCH(QTextCharFormat, format2);
|
||||
QFETCH(bool, overrideAll);
|
||||
|
||||
QTextCharFormat f;
|
||||
|
||||
f.merge(format1);
|
||||
QCOMPARE((int)f.fontStyleHint(), (int)format1.fontStyleHint());
|
||||
QCOMPARE((int)f.fontStyleStrategy(), (int)format1.fontStyleStrategy());
|
||||
QCOMPARE((int)f.fontCapitalization(), (int)format1.fontCapitalization());
|
||||
QCOMPARE(f.fontKerning(), format1.fontKerning());
|
||||
|
||||
QCOMPARE((int)f.font().styleHint(), (int)f.fontStyleHint());
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)f.fontStyleStrategy());
|
||||
QCOMPARE((int)f.font().capitalization(), (int)f.fontCapitalization());
|
||||
QCOMPARE(f.font().kerning(), f.fontKerning());
|
||||
|
||||
f.merge(format2);
|
||||
QCOMPARE((int)f.font().styleHint(), (int)f.fontStyleHint());
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)f.fontStyleStrategy());
|
||||
QCOMPARE((int)f.font().capitalization(), (int)f.fontCapitalization());
|
||||
QCOMPARE(f.font().kerning(), f.fontKerning());
|
||||
|
||||
if (format2.hasProperty(QTextFormat::FontStyleHint))
|
||||
QCOMPARE((int)f.font().styleHint(), (int)format2.fontStyleHint());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleHint(), (int)format1.fontStyleHint());
|
||||
if (format2.hasProperty(QTextFormat::FontStyleStrategy))
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)format2.fontStyleStrategy());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)format1.fontStyleStrategy());
|
||||
if (format2.hasProperty(QTextFormat::FontCapitalization))
|
||||
QCOMPARE((int)f.font().capitalization(), (int)format2.fontCapitalization());
|
||||
else
|
||||
QCOMPARE((int)f.font().capitalization(), (int)format1.fontCapitalization());
|
||||
if (format2.hasProperty(QTextFormat::FontKerning))
|
||||
QCOMPARE(f.font().kerning(), format2.fontKerning());
|
||||
else
|
||||
QCOMPARE(f.font().kerning(), format1.fontKerning());
|
||||
|
||||
|
||||
QFont font1 = format1.font();
|
||||
QFont font2 = format2.font();
|
||||
|
||||
f = QTextCharFormat();
|
||||
|
||||
{
|
||||
QTextCharFormat tmp;
|
||||
tmp.setFont(font1, overrideAll ? QTextCharFormat::FontPropertiesAll
|
||||
: QTextCharFormat::FontPropertiesSpecifiedOnly);
|
||||
f.merge(tmp);
|
||||
}
|
||||
QCOMPARE((int)f.fontStyleHint(), (int)format1.fontStyleHint());
|
||||
QCOMPARE((int)f.fontStyleStrategy(), (int)format1.fontStyleStrategy());
|
||||
QCOMPARE((int)f.fontCapitalization(), (int)format1.fontCapitalization());
|
||||
QCOMPARE(f.fontKerning(), format1.fontKerning());
|
||||
|
||||
QCOMPARE((int)f.font().styleHint(), (int)f.fontStyleHint());
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)f.fontStyleStrategy());
|
||||
QCOMPARE((int)f.font().capitalization(), (int)f.fontCapitalization());
|
||||
QCOMPARE(f.font().kerning(), f.fontKerning());
|
||||
|
||||
{
|
||||
QTextCharFormat tmp;
|
||||
tmp.setFont(font2, overrideAll ? QTextCharFormat::FontPropertiesAll
|
||||
: QTextCharFormat::FontPropertiesSpecifiedOnly);
|
||||
f.merge(tmp);
|
||||
}
|
||||
QCOMPARE((int)f.font().styleHint(), (int)f.fontStyleHint());
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)f.fontStyleStrategy());
|
||||
QCOMPARE((int)f.font().capitalization(), (int)f.fontCapitalization());
|
||||
QCOMPARE(f.font().kerning(), f.fontKerning());
|
||||
|
||||
if (overrideAll || (font2.resolveMask() & QFont::StyleHintResolved))
|
||||
QCOMPARE((int)f.font().styleHint(), (int)font2.styleHint());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleHint(), (int)font1.styleHint());
|
||||
if (overrideAll || (font2.resolveMask() & QFont::StyleStrategyResolved))
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)font2.styleStrategy());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)font1.styleStrategy());
|
||||
if (overrideAll || (font2.resolveMask() & QFont::CapitalizationResolved))
|
||||
QCOMPARE((int)f.font().capitalization(), (int)font2.capitalization());
|
||||
else
|
||||
QCOMPARE((int)f.font().capitalization(), (int)font1.capitalization());
|
||||
if (overrideAll || (font2.resolveMask() & QFont::KerningResolved))
|
||||
QCOMPARE(f.font().kerning(), font2.kerning());
|
||||
else
|
||||
QCOMPARE(f.font().kerning(), font1.kerning());
|
||||
}
|
||||
|
||||
void tst_QTextFormat::setFont_collection_data()
|
||||
{
|
||||
setFont_data();
|
||||
}
|
||||
|
||||
void tst_QTextFormat::setFont_collection()
|
||||
{
|
||||
QFETCH(QTextCharFormat, format1);
|
||||
QFETCH(QTextCharFormat, format2);
|
||||
QFETCH(bool, overrideAll);
|
||||
|
||||
QFont font1 = format1.font();
|
||||
QFont font2 = format2.font();
|
||||
|
||||
{
|
||||
QTextFormatCollection collection;
|
||||
collection.setDefaultFont(font1);
|
||||
|
||||
int formatIndex = collection.indexForFormat(format1);
|
||||
QTextCharFormat f = collection.charFormat(formatIndex);
|
||||
|
||||
QCOMPARE((int)f.fontStyleHint(), (int)format1.fontStyleHint());
|
||||
QCOMPARE((int)f.fontStyleStrategy(), (int)format1.fontStyleStrategy());
|
||||
QCOMPARE((int)f.fontCapitalization(), (int)format1.fontCapitalization());
|
||||
QCOMPARE(f.fontKerning(), format1.fontKerning());
|
||||
|
||||
QCOMPARE((int)f.font().styleHint(), (int)f.fontStyleHint());
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)f.fontStyleStrategy());
|
||||
QCOMPARE((int)f.font().capitalization(), (int)f.fontCapitalization());
|
||||
QCOMPARE(f.font().kerning(), f.fontKerning());
|
||||
}
|
||||
{
|
||||
QTextFormatCollection collection;
|
||||
collection.setDefaultFont(font1);
|
||||
|
||||
int formatIndex = collection.indexForFormat(format2);
|
||||
QTextCharFormat f = collection.charFormat(formatIndex);
|
||||
|
||||
if (format2.hasProperty(QTextFormat::FontStyleHint))
|
||||
QCOMPARE((int)f.font().styleHint(), (int)format2.fontStyleHint());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleHint(), (int)format1.fontStyleHint());
|
||||
if (format2.hasProperty(QTextFormat::FontStyleStrategy))
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)format2.fontStyleStrategy());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)format1.fontStyleStrategy());
|
||||
if (format2.hasProperty(QTextFormat::FontCapitalization))
|
||||
QCOMPARE((int)f.font().capitalization(), (int)format2.fontCapitalization());
|
||||
else
|
||||
QCOMPARE((int)f.font().capitalization(), (int)format1.fontCapitalization());
|
||||
if (format2.hasProperty(QTextFormat::FontKerning))
|
||||
QCOMPARE(f.font().kerning(), format2.fontKerning());
|
||||
else
|
||||
QCOMPARE(f.font().kerning(), format1.fontKerning());
|
||||
}
|
||||
|
||||
{
|
||||
QTextFormatCollection collection;
|
||||
collection.setDefaultFont(font1);
|
||||
|
||||
QTextCharFormat tmp;
|
||||
tmp.setFont(font1, overrideAll ? QTextCharFormat::FontPropertiesAll
|
||||
: QTextCharFormat::FontPropertiesSpecifiedOnly);
|
||||
int formatIndex = collection.indexForFormat(tmp);
|
||||
QTextCharFormat f = collection.charFormat(formatIndex);
|
||||
|
||||
QCOMPARE((int)f.fontStyleHint(), (int)format1.fontStyleHint());
|
||||
QCOMPARE((int)f.fontStyleStrategy(), (int)format1.fontStyleStrategy());
|
||||
QCOMPARE((int)f.fontCapitalization(), (int)format1.fontCapitalization());
|
||||
QCOMPARE(f.fontKerning(), format1.fontKerning());
|
||||
|
||||
QCOMPARE((int)f.font().styleHint(), (int)f.fontStyleHint());
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)f.fontStyleStrategy());
|
||||
QCOMPARE((int)f.font().capitalization(), (int)f.fontCapitalization());
|
||||
QCOMPARE(f.font().kerning(), f.fontKerning());
|
||||
}
|
||||
{
|
||||
QTextFormatCollection collection;
|
||||
collection.setDefaultFont(font1);
|
||||
|
||||
QTextCharFormat tmp;
|
||||
tmp.setFont(font2, overrideAll ? QTextCharFormat::FontPropertiesAll
|
||||
: QTextCharFormat::FontPropertiesSpecifiedOnly);
|
||||
int formatIndex = collection.indexForFormat(tmp);
|
||||
QTextCharFormat f = collection.charFormat(formatIndex);
|
||||
|
||||
if (overrideAll || (font2.resolveMask() & QFont::StyleHintResolved))
|
||||
QCOMPARE((int)f.font().styleHint(), (int)font2.styleHint());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleHint(), (int)font1.styleHint());
|
||||
if (overrideAll || (font2.resolveMask() & QFont::StyleStrategyResolved))
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)font2.styleStrategy());
|
||||
else
|
||||
QCOMPARE((int)f.font().styleStrategy(), (int)font1.styleStrategy());
|
||||
if (overrideAll || (font2.resolveMask() & QFont::CapitalizationResolved))
|
||||
QCOMPARE((int)f.font().capitalization(), (int)font2.capitalization());
|
||||
else
|
||||
QCOMPARE((int)f.font().capitalization(), (int)font1.capitalization());
|
||||
if (overrideAll || (font2.resolveMask() & QFont::KerningResolved))
|
||||
QCOMPARE(f.font().kerning(), font2.kerning());
|
||||
else
|
||||
QCOMPARE(f.font().kerning(), font1.kerning());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextFormat::clearCollection()
|
||||
{
|
||||
QTextFormatCollection collection;
|
||||
QFont f;
|
||||
f.setUnderline(true);
|
||||
collection.setDefaultFont(f);
|
||||
QTextCharFormat charFormat;
|
||||
charFormat.setFontStyleHint(QFont::SansSerif);
|
||||
int formatIndex = collection.indexForFormat(charFormat);
|
||||
QCOMPARE(formatIndex, 0);
|
||||
QTextCharFormat charFormat2;
|
||||
charFormat2.setUnderlineStyle(QTextCharFormat::SingleUnderline);
|
||||
int formatIndex2 = collection.indexForFormat(charFormat2);
|
||||
QCOMPARE(formatIndex2, 1);
|
||||
QCOMPARE(collection.formats.size(), 2);
|
||||
QCOMPARE(collection.hashes.size(), 2);
|
||||
QCOMPARE(collection.defaultFont(), f);
|
||||
|
||||
collection.clear();
|
||||
QCOMPARE(collection.formats.size(), 0);
|
||||
QCOMPARE(collection.hashes.size(), 0);
|
||||
QCOMPARE(collection.indexForFormat(charFormat2), 0);
|
||||
QCOMPARE(collection.formats.size(), 1);
|
||||
QCOMPARE(collection.hashes.size(), 1);
|
||||
QCOMPARE(collection.defaultFont(), f); // kept, QTextDocument::clear or setPlainText should not reset the font set by setDefaultFont
|
||||
}
|
||||
|
||||
#ifndef QT_NO_DATASTREAM
|
||||
void tst_QTextFormat::dataStreamCompatibility()
|
||||
{
|
||||
// Make sure that we are still compatible with the old values of QTextFormat::FontLetterSpacingType
|
||||
// and QTextFormat::FontStretch, when used with earlier QDataStream versions
|
||||
QTextCharFormat format;
|
||||
format.setFontStretch(42);
|
||||
format.setFontLetterSpacingType(QFont::AbsoluteSpacing);
|
||||
format.setFontFamilies({QLatin1String("Arial")});
|
||||
|
||||
// Sanity check
|
||||
{
|
||||
QMap<int, QVariant> properties = format.properties();
|
||||
QVERIFY(properties.contains(QTextFormat::FontLetterSpacingType));
|
||||
QVERIFY(properties.contains(QTextFormat::FontStretch));
|
||||
QVERIFY(properties.contains(QTextFormat::FontFamilies));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontLetterSpacingType));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontStretch));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontFamily));
|
||||
}
|
||||
|
||||
QByteArray memory;
|
||||
|
||||
// Current stream version
|
||||
{
|
||||
{
|
||||
QBuffer buffer(&memory);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
QDataStream stream(&buffer);
|
||||
stream << format;
|
||||
}
|
||||
|
||||
{
|
||||
QBuffer buffer(&memory);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
QDataStream stream(&buffer);
|
||||
|
||||
QTextFormat other;
|
||||
stream >> other;
|
||||
|
||||
{
|
||||
QMap<int, QVariant> properties = other.properties();
|
||||
QVERIFY(properties.contains(QTextFormat::FontLetterSpacingType));
|
||||
QVERIFY(properties.contains(QTextFormat::FontStretch));
|
||||
QVERIFY(properties.contains(QTextFormat::FontFamilies));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontLetterSpacingType));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontStretch));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontFamily));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QBuffer buffer(&memory);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
QDataStream stream(&buffer);
|
||||
|
||||
quint32 type;
|
||||
stream >> type;
|
||||
|
||||
QMap<qint32, QVariant> properties;
|
||||
stream >> properties;
|
||||
QVERIFY(properties.contains(QTextFormat::FontLetterSpacingType));
|
||||
QVERIFY(properties.contains(QTextFormat::FontStretch));
|
||||
QVERIFY(properties.contains(QTextFormat::FontFamilies));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontLetterSpacingType));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontStretch));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontFamily));
|
||||
}
|
||||
}
|
||||
|
||||
// Qt 5.15 stream version
|
||||
memory.clear();
|
||||
{
|
||||
{
|
||||
QBuffer buffer(&memory);
|
||||
buffer.open(QIODevice::WriteOnly);
|
||||
|
||||
QDataStream stream(&buffer);
|
||||
stream.setVersion(QDataStream::Qt_5_15);
|
||||
stream << format;
|
||||
}
|
||||
|
||||
{
|
||||
QBuffer buffer(&memory);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
QDataStream stream(&buffer);
|
||||
stream.setVersion(QDataStream::Qt_5_15);
|
||||
|
||||
QTextFormat other;
|
||||
stream >> other;
|
||||
|
||||
{
|
||||
QMap<int, QVariant> properties = other.properties();
|
||||
QVERIFY(properties.contains(QTextFormat::FontLetterSpacingType));
|
||||
QVERIFY(properties.contains(QTextFormat::FontStretch));
|
||||
QVERIFY(properties.contains(QTextFormat::FontFamilies));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontLetterSpacingType));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontStretch));
|
||||
QVERIFY(!properties.contains(QTextFormat::OldFontFamily));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
QBuffer buffer(&memory);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
|
||||
QDataStream stream(&buffer);
|
||||
stream.setVersion(QDataStream::Qt_5_15);
|
||||
|
||||
quint32 type;
|
||||
stream >> type;
|
||||
|
||||
// Verify that old data stream still has the compatibility values
|
||||
QMap<qint32, QVariant> properties;
|
||||
stream >> properties;
|
||||
QVERIFY(!properties.contains(QTextFormat::FontLetterSpacingType));
|
||||
QVERIFY(!properties.contains(QTextFormat::FontStretch));
|
||||
QVERIFY(!properties.contains(QTextFormat::FontFamilies));
|
||||
QVERIFY(properties.contains(QTextFormat::OldFontLetterSpacingType));
|
||||
QVERIFY(properties.contains(QTextFormat::OldFontStretch));
|
||||
QVERIFY(properties.contains(QTextFormat::OldFontFamily));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
#endif // QT_NO_DATASTREAM
|
||||
|
||||
QTEST_MAIN(tst_QTextFormat)
|
||||
#include "tst_qtextformat.moc"
|
23
tests/auto/gui/text/qtextimagehandler/CMakeLists.txt
Normal file
23
tests/auto/gui/text/qtextimagehandler/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
list(APPEND test_data "data/image.png")
|
||||
list(APPEND test_data "data/image@2x.png")
|
||||
|
||||
qt_internal_add_test(tst_qtextimagehandler
|
||||
SOURCES
|
||||
tst_qtextimagehandler.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
TESTDATA
|
||||
${test_data}
|
||||
)
|
||||
|
||||
qt_internal_add_resource(tst_qtextimagehandler "qtextimagehandler"
|
||||
PREFIX
|
||||
"/"
|
||||
FILES
|
||||
${test_data}
|
||||
)
|
BIN
tests/auto/gui/text/qtextimagehandler/data/image.png
Normal file
BIN
tests/auto/gui/text/qtextimagehandler/data/image.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 101 B |
BIN
tests/auto/gui/text/qtextimagehandler/data/image@2x.png
Normal file
BIN
tests/auto/gui/text/qtextimagehandler/data/image@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 102 B |
@ -0,0 +1,79 @@
|
||||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <QPainter>
|
||||
#include <private/qtextimagehandler_p.h>
|
||||
|
||||
class tst_QTextImageHandler : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QTextImageHandler();
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
void cleanupTestCase();
|
||||
void loadAtNImages_data();
|
||||
void loadAtNImages();
|
||||
};
|
||||
|
||||
tst_QTextImageHandler::tst_QTextImageHandler()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QTextImageHandler::init()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QTextImageHandler::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QTextImageHandler::cleanupTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QTextImageHandler::loadAtNImages_data()
|
||||
{
|
||||
QTest::addColumn<QString>("imageFile");
|
||||
|
||||
QTest::addRow("file") << QFINDTESTDATA("data/image.png");
|
||||
QTest::addRow("file_url") << QString("file:/") + QFINDTESTDATA("data/image.png");
|
||||
QTest::addRow("resource") << ":/data/image.png";
|
||||
QTest::addRow("qrc_url") << "qrc:/data/image.png";
|
||||
}
|
||||
|
||||
void tst_QTextImageHandler::loadAtNImages()
|
||||
{
|
||||
QFETCH(QString, imageFile);
|
||||
|
||||
QTextDocument doc;
|
||||
QTextCursor c(&doc);
|
||||
c.insertHtml("<img src=\"" + imageFile + "\">");
|
||||
const auto formats = doc.allFormats();
|
||||
const auto it = std::find_if(formats.begin(), formats.end(), [](const auto &format){
|
||||
return format.objectType() == QTextFormat::ImageObject;
|
||||
});
|
||||
QVERIFY(it != formats.end());
|
||||
const QTextImageFormat format = (*it).toImageFormat();
|
||||
QTextImageHandler handler;
|
||||
|
||||
for (const auto &dpr : {1, 2}) {
|
||||
QImage img(20, 20, QImage::Format_ARGB32_Premultiplied);
|
||||
img.fill(Qt::white);
|
||||
img.setDevicePixelRatio(dpr);
|
||||
QPainter p(&img);
|
||||
handler.drawObject(&p, QRect(0, 0, 20, 20), &doc, 0, format);
|
||||
p.end();
|
||||
QVERIFY(!img.isNull());
|
||||
const auto expectedColor = dpr == 1 ? Qt::red : Qt::green;
|
||||
QCOMPARE(img.pixelColor(0, 0), expectedColor);
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextImageHandler)
|
||||
#include "tst_qtextimagehandler.moc"
|
15
tests/auto/gui/text/qtextlayout/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qtextlayout/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextlayout Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextlayout
|
||||
SOURCES
|
||||
tst_qtextlayout.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
2749
tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp
Normal file
2749
tests/auto/gui/text/qtextlayout/tst_qtextlayout.cpp
Normal file
File diff suppressed because it is too large
Load Diff
16
tests/auto/gui/text/qtextlist/CMakeLists.txt
Normal file
16
tests/auto/gui/text/qtextlist/CMakeLists.txt
Normal file
@ -0,0 +1,16 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextlist Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextlist
|
||||
SOURCES
|
||||
../qtextdocument/common.h
|
||||
tst_qtextlist.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
404
tests/auto/gui/text/qtextlist/tst_qtextlist.cpp
Normal file
404
tests/auto/gui/text/qtextlist/tst_qtextlist.cpp
Normal file
@ -0,0 +1,404 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <qtextdocument.h>
|
||||
#include <qtextdocumentfragment.h>
|
||||
#include <qtextlist.h>
|
||||
#include <qabstracttextdocumentlayout.h>
|
||||
#include <qtextcursor.h>
|
||||
#include "../qtextdocument/common.h"
|
||||
|
||||
class tst_QTextList : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
void item();
|
||||
void autoNumbering();
|
||||
void autoNumberingRTL();
|
||||
void autoNumberingPrefixAndSuffix();
|
||||
void autoNumberingPrefixAndSuffixRTL();
|
||||
void autoNumberingPrefixAndSuffixHtmlExportImport();
|
||||
void romanNumbering();
|
||||
void romanNumberingLimit();
|
||||
void formatChange();
|
||||
void cursorNavigation();
|
||||
void partialRemoval();
|
||||
void formatReferenceChange();
|
||||
void ensureItemOrder();
|
||||
void add();
|
||||
void defaultIndent();
|
||||
void blockUpdate();
|
||||
void numbering_data();
|
||||
void numbering();
|
||||
|
||||
private:
|
||||
QTextDocument *doc;
|
||||
QTextCursor cursor;
|
||||
QTestDocumentLayout *layout;
|
||||
};
|
||||
|
||||
void tst_QTextList::init()
|
||||
{
|
||||
doc = new QTextDocument();
|
||||
layout = new QTestDocumentLayout(doc);
|
||||
doc->setDocumentLayout(layout);
|
||||
cursor = QTextCursor(doc);
|
||||
}
|
||||
|
||||
void tst_QTextList::cleanup()
|
||||
{
|
||||
cursor = QTextCursor();
|
||||
delete doc;
|
||||
doc = 0;
|
||||
}
|
||||
|
||||
void tst_QTextList::item()
|
||||
{
|
||||
// this is basically a test for the key() + 1 in QTextList::item.
|
||||
QTextList *list = cursor.createList(QTextListFormat());
|
||||
QVERIFY(list->item(0).blockFormat().objectIndex() != -1);
|
||||
}
|
||||
|
||||
void tst_QTextList::autoNumbering()
|
||||
{
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::ListLowerAlpha);
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
for (int i = 0; i < 27; ++i)
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), 28);
|
||||
|
||||
QVERIFY(cursor.currentList());
|
||||
QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 27);
|
||||
QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("ab."));
|
||||
}
|
||||
|
||||
void tst_QTextList::autoNumberingPrefixAndSuffix()
|
||||
{
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::ListLowerAlpha);
|
||||
fmt.setNumberPrefix("-");
|
||||
fmt.setNumberSuffix(")");
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
for (int i = 0; i < 27; ++i)
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), 28);
|
||||
|
||||
QVERIFY(cursor.currentList());
|
||||
QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 27);
|
||||
QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("-ab)"));
|
||||
}
|
||||
|
||||
void tst_QTextList::autoNumberingPrefixAndSuffixRTL()
|
||||
{
|
||||
QTextBlockFormat bfmt;
|
||||
bfmt.setLayoutDirection(Qt::RightToLeft);
|
||||
cursor.setBlockFormat(bfmt);
|
||||
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::ListUpperAlpha);
|
||||
fmt.setNumberPrefix("-");
|
||||
fmt.setNumberSuffix("*");
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), 2);
|
||||
|
||||
QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("*B-"));
|
||||
}
|
||||
|
||||
void tst_QTextList::autoNumberingPrefixAndSuffixHtmlExportImport()
|
||||
{
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::ListLowerAlpha);
|
||||
fmt.setNumberPrefix("\"");
|
||||
fmt.setNumberSuffix("#");
|
||||
fmt.setIndent(10);
|
||||
// FIXME: Would like to test "'" but there's a problem in the css parser (Scanner::preprocess
|
||||
// is called before the values are being parsed), so the quoting does not work.
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
for (int i = 0; i < 27; ++i)
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), 28);
|
||||
|
||||
QString htmlExport = doc->toHtml();
|
||||
QTextDocument importDoc;
|
||||
importDoc.setHtml(htmlExport);
|
||||
|
||||
QTextCursor importCursor(&importDoc);
|
||||
for (int i = 0; i < 27; ++i)
|
||||
importCursor.movePosition(QTextCursor::NextBlock);
|
||||
|
||||
QVERIFY(importCursor.currentList());
|
||||
QCOMPARE(importCursor.currentList()->itemNumber(importCursor.block()), 27);
|
||||
QCOMPARE(importCursor.currentList()->itemText(importCursor.block()), QLatin1String("\"ab#"));
|
||||
QCOMPARE(importCursor.currentList()->format().indent(), 10);
|
||||
}
|
||||
|
||||
void tst_QTextList::autoNumberingRTL()
|
||||
{
|
||||
QTextBlockFormat bfmt;
|
||||
bfmt.setLayoutDirection(Qt::RightToLeft);
|
||||
cursor.setBlockFormat(bfmt);
|
||||
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::ListUpperAlpha);
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), 2);
|
||||
|
||||
QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String(".B"));
|
||||
}
|
||||
|
||||
void tst_QTextList::romanNumbering()
|
||||
{
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::ListUpperRoman);
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
for (int i = 0; i < 4998; ++i)
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), 4999);
|
||||
|
||||
QVERIFY(cursor.currentList());
|
||||
QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 4998);
|
||||
QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("MMMMCMXCIX."));
|
||||
}
|
||||
|
||||
void tst_QTextList::romanNumberingLimit()
|
||||
{
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::ListLowerRoman);
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
for (int i = 0; i < 4999; ++i)
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), 5000);
|
||||
|
||||
QVERIFY(cursor.currentList());
|
||||
QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 4999);
|
||||
QCOMPARE(cursor.currentList()->itemText(cursor.block()), QLatin1String("?."));
|
||||
}
|
||||
|
||||
void tst_QTextList::formatChange()
|
||||
{
|
||||
// testing the formatChanged slot in QTextListManager
|
||||
|
||||
/* <initial block>
|
||||
* 1.
|
||||
* 2.
|
||||
*/
|
||||
QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
|
||||
QTextList *firstList = list;
|
||||
cursor.insertBlock();
|
||||
|
||||
QVERIFY(list && list->count() == 2);
|
||||
|
||||
QTextBlockFormat bfmt = cursor.blockFormat();
|
||||
// QCOMPARE(bfmt.object(), list);
|
||||
|
||||
bfmt.setObjectIndex(-1);
|
||||
cursor.setBlockFormat(bfmt);
|
||||
|
||||
QCOMPARE(firstList->count(), 1);
|
||||
}
|
||||
|
||||
void tst_QTextList::cursorNavigation()
|
||||
{
|
||||
// testing some cursor list methods
|
||||
|
||||
/* <initial block>
|
||||
* 1.
|
||||
* 2.
|
||||
*/
|
||||
cursor.insertList(QTextListFormat::ListDecimal);
|
||||
cursor.insertBlock();
|
||||
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.movePosition(QTextCursor::NextBlock);
|
||||
cursor.movePosition(QTextCursor::NextBlock);
|
||||
QVERIFY(cursor.currentList());
|
||||
cursor.movePosition(QTextCursor::PreviousBlock);
|
||||
QVERIFY(cursor.currentList());
|
||||
QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), 0);
|
||||
}
|
||||
|
||||
void tst_QTextList::partialRemoval()
|
||||
{
|
||||
/* this is essentially a test for PieceTable::removeBlock to not miss any
|
||||
blocks with the blockChanged signal emission that actually get removed.
|
||||
|
||||
It creates two lists, like this:
|
||||
|
||||
1. Hello World
|
||||
a. Foobar
|
||||
|
||||
and then removes from within the 'Hello World' into the 'Foobar' .
|
||||
There used to be no emission for the removal of the second (a.) block,
|
||||
causing list inconsistencies.
|
||||
|
||||
*/
|
||||
|
||||
QTextList *firstList = cursor.insertList(QTextListFormat::ListDecimal);
|
||||
|
||||
QTextCursor selStart = cursor;
|
||||
selStart.movePosition(QTextCursor::PreviousCharacter);
|
||||
|
||||
cursor.insertText("Hello World");
|
||||
|
||||
// position it well into the 'hello world' text.
|
||||
selStart.movePosition(QTextCursor::NextCharacter);
|
||||
selStart.movePosition(QTextCursor::NextCharacter);
|
||||
selStart.clearSelection();
|
||||
|
||||
QPointer<QTextList> secondList = cursor.insertList(QTextListFormat::ListCircle);
|
||||
cursor.insertText("Foobar");
|
||||
|
||||
// position it into the 'foo bar' text.
|
||||
cursor.movePosition(QTextCursor::PreviousCharacter);
|
||||
QTextCursor selEnd = cursor;
|
||||
|
||||
// this creates a selection that includes parts of both text-fragments and also the list item of the second list.
|
||||
QTextCursor selection = selStart;
|
||||
selection.setPosition(selEnd.position(), QTextCursor::KeepAnchor);
|
||||
|
||||
selection.deleteChar(); // deletes the second list
|
||||
|
||||
QVERIFY(!secondList);
|
||||
QVERIFY(firstList->count() > 0);
|
||||
|
||||
doc->undo();
|
||||
}
|
||||
|
||||
void tst_QTextList::formatReferenceChange()
|
||||
{
|
||||
QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
|
||||
cursor.insertText("Some Content...");
|
||||
cursor.insertBlock(QTextBlockFormat());
|
||||
|
||||
cursor.setPosition(list->item(0).position());
|
||||
int listItemStartPos = cursor.position();
|
||||
cursor.movePosition(QTextCursor::NextBlock);
|
||||
int listItemLen = cursor.position() - listItemStartPos;
|
||||
layout->expect(listItemStartPos, listItemLen, listItemLen);
|
||||
|
||||
QTextListFormat fmt = list->format();
|
||||
fmt.setStyle(QTextListFormat::ListCircle);
|
||||
list->setFormat(fmt);
|
||||
|
||||
QVERIFY(layout->called);
|
||||
QVERIFY(!layout->error);
|
||||
}
|
||||
|
||||
void tst_QTextList::ensureItemOrder()
|
||||
{
|
||||
/*
|
||||
* Insert a new list item before the first one and verify the blocks
|
||||
* are sorted after that.
|
||||
*/
|
||||
QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
|
||||
|
||||
QTextBlockFormat fmt = cursor.blockFormat();
|
||||
cursor.movePosition(QTextCursor::Start);
|
||||
cursor.insertBlock(fmt);
|
||||
|
||||
QCOMPARE(list->item(0).position(), 1);
|
||||
QCOMPARE(list->item(1).position(), 2);
|
||||
}
|
||||
|
||||
void tst_QTextList::add()
|
||||
{
|
||||
QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
|
||||
cursor.insertBlock(QTextBlockFormat());
|
||||
QCOMPARE(list->count(), 1);
|
||||
cursor.insertBlock(QTextBlockFormat());
|
||||
list->add(cursor.block());
|
||||
QCOMPARE(list->count(), 2);
|
||||
}
|
||||
|
||||
// Task #72036
|
||||
void tst_QTextList::defaultIndent()
|
||||
{
|
||||
QTextListFormat fmt;
|
||||
QCOMPARE(fmt.indent(), 1);
|
||||
}
|
||||
|
||||
void tst_QTextList::blockUpdate()
|
||||
{
|
||||
// three items
|
||||
QTextList *list = cursor.insertList(QTextListFormat::ListDecimal);
|
||||
cursor.insertBlock();
|
||||
cursor.insertBlock();
|
||||
|
||||
// remove second, needs also update on the third
|
||||
// since the numbering might have changed
|
||||
const int len = cursor.position() + cursor.block().length() - 1;
|
||||
layout->expect(1, len, len);
|
||||
list->remove(list->item(1));
|
||||
QVERIFY(!layout->error);
|
||||
}
|
||||
|
||||
void tst_QTextList::numbering_data()
|
||||
{
|
||||
QTest::addColumn<int>("format");
|
||||
QTest::addColumn<int>("number");
|
||||
QTest::addColumn<QString>("result");
|
||||
|
||||
QTest::newRow("E.") << int(QTextListFormat::ListUpperAlpha) << 5 << "E.";
|
||||
QTest::newRow("abc.") << int(QTextListFormat::ListLowerAlpha) << (26 + 2) * 26 + 3 << "abc.";
|
||||
QTest::newRow("12.") << int(QTextListFormat::ListDecimal) << 12 << "12.";
|
||||
QTest::newRow("XXIV.") << int(QTextListFormat::ListUpperRoman) << 24 << "XXIV.";
|
||||
QTest::newRow("VIII.") << int(QTextListFormat::ListUpperRoman) << 8 << "VIII.";
|
||||
QTest::newRow("xxx.") << int(QTextListFormat::ListLowerRoman) << 30 << "xxx.";
|
||||
QTest::newRow("xxix.") << int(QTextListFormat::ListLowerRoman) << 29 << "xxix.";
|
||||
// QTest::newRow("xxx. alpha") << int(QTextListFormat::ListLowerAlpha) << (24 * 26 + 24) * 26 + 24 << "xxx."; //Too slow
|
||||
}
|
||||
|
||||
void tst_QTextList::numbering()
|
||||
{
|
||||
QFETCH(int, format);
|
||||
QFETCH(int, number);
|
||||
QFETCH(QString, result);
|
||||
|
||||
|
||||
QTextListFormat fmt;
|
||||
fmt.setStyle(QTextListFormat::Style(format));
|
||||
QTextList *list = cursor.createList(fmt);
|
||||
QVERIFY(list);
|
||||
|
||||
for (int i = 1; i < number; ++i)
|
||||
cursor.insertBlock();
|
||||
|
||||
QCOMPARE(list->count(), number);
|
||||
|
||||
QVERIFY(cursor.currentList());
|
||||
QCOMPARE(cursor.currentList()->itemNumber(cursor.block()), number - 1);
|
||||
QCOMPARE(cursor.currentList()->itemText(cursor.block()), result);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextList)
|
||||
#include "tst_qtextlist.moc"
|
22
tests/auto/gui/text/qtextmarkdownimporter/CMakeLists.txt
Normal file
22
tests/auto/gui/text/qtextmarkdownimporter/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextmarkdownimporter Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "data/thematicBreaks.md")
|
||||
list(APPEND test_data "data/headingBulletsContinuations.md")
|
||||
list(APPEND test_data "data/fuzz20450.md")
|
||||
list(APPEND test_data "data/fuzz20580.md")
|
||||
|
||||
qt_internal_add_test(tst_qtextmarkdownimporter
|
||||
SOURCES
|
||||
tst_qtextmarkdownimporter.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
@ -0,0 +1,5 @@
|
||||
<t><EFBFBD>
|
||||
* <20>
|
||||
|
||||
<09>
|
||||
* <20>
|
@ -0,0 +1 @@
|
||||
|
|
@ -0,0 +1,28 @@
|
||||
# heading
|
||||
- bullet 1
|
||||
continuation line 1, indented via tab
|
||||
- bullet 2
|
||||
continuation line 2, indented via 4 spaces
|
||||
- bullet 3
|
||||
|
||||
continuation paragraph 3, indented via tab
|
||||
|
||||
- bullet 3.1
|
||||
|
||||
continuation paragraph 3.1, indented via 4 spaces
|
||||
|
||||
- bullet 3.2
|
||||
continuation line, indented via 2 tabs
|
||||
- bullet 4
|
||||
|
||||
continuation paragraph 4, indented via 4 spaces
|
||||
and continuing onto another line too
|
||||
|
||||
- bullet 5
|
||||
|
||||
continuation paragraph 5, indented via 2 spaces and continuing onto another
|
||||
line too
|
||||
|
||||
- bullet 6
|
||||
|
||||
plain old paragraph at the end
|
@ -0,0 +1,17 @@
|
||||
Heading
|
||||
-------
|
||||
***
|
||||
stars
|
||||
- bullet
|
||||
** not a bullet or a rule, just two stars
|
||||
- [ ] unchecked
|
||||
|
||||
--- indented too far, so not a rule
|
||||
* * *
|
||||
stars with tabs between
|
||||
***
|
||||
stars with whitespace after
|
||||
---
|
||||
hyphens with whitespace after
|
||||
_____
|
||||
underscores with whitespace after
|
@ -0,0 +1,568 @@
|
||||
// Copyright (C) 2019 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#include <QBuffer>
|
||||
#include <QDebug>
|
||||
#include <QFontInfo>
|
||||
#include <QTextDocument>
|
||||
#include <QTextCursor>
|
||||
#include <QTextBlock>
|
||||
#include <QTextDocumentFragment>
|
||||
#include <QTextList>
|
||||
#include <QTextTable>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include <private/qtextmarkdownimporter_p.h>
|
||||
|
||||
// #define DEBUG_WRITE_HTML
|
||||
|
||||
Q_LOGGING_CATEGORY(lcTests, "qt.text.tests")
|
||||
|
||||
static const QChar LineBreak = QChar(0x2028);
|
||||
static const QChar Tab = QLatin1Char('\t');
|
||||
static const QChar Space = QLatin1Char(' ');
|
||||
static const QChar Period = QLatin1Char('.');
|
||||
|
||||
class tst_QTextMarkdownImporter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void headingBulletsContinuations();
|
||||
void thematicBreaks();
|
||||
void lists_data();
|
||||
void lists();
|
||||
void nestedSpans_data();
|
||||
void nestedSpans();
|
||||
void avoidBlankLineAtBeginning_data();
|
||||
void avoidBlankLineAtBeginning();
|
||||
void fragmentsAndProperties_data();
|
||||
void fragmentsAndProperties();
|
||||
void pathological_data();
|
||||
void pathological();
|
||||
void fencedCodeBlocks_data();
|
||||
void fencedCodeBlocks();
|
||||
|
||||
private:
|
||||
bool isMainFontFixed();
|
||||
|
||||
public:
|
||||
enum CharFormat {
|
||||
Normal = 0x0,
|
||||
Italic = 0x1,
|
||||
Bold = 0x02,
|
||||
Underlined = 0x04,
|
||||
Strikeout = 0x08,
|
||||
Mono = 0x10,
|
||||
Link = 0x20
|
||||
};
|
||||
Q_ENUM(CharFormat)
|
||||
Q_DECLARE_FLAGS(CharFormats, CharFormat)
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(tst_QTextMarkdownImporter::CharFormats)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(tst_QTextMarkdownImporter::CharFormats)
|
||||
|
||||
bool tst_QTextMarkdownImporter::isMainFontFixed()
|
||||
{
|
||||
bool ret = QFontInfo(QGuiApplication::font()).fixedPitch();
|
||||
if (ret) {
|
||||
qCWarning(lcTests) << "QFontDatabase::GeneralFont is monospaced: markdown writing is likely to use too many backticks";
|
||||
qCWarning(lcTests) << "system fonts: fixed" << QFontDatabase::systemFont(QFontDatabase::FixedFont)
|
||||
<< "fixed?" << QFontInfo(QFontDatabase::systemFont(QFontDatabase::FixedFont)).fixedPitch()
|
||||
<< "general" << QFontDatabase::systemFont(QFontDatabase::GeneralFont);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::headingBulletsContinuations()
|
||||
{
|
||||
const QStringList expectedBlocks = QStringList() <<
|
||||
"heading" <<
|
||||
"bullet 1 continuation line 1, indented via tab" <<
|
||||
"bullet 2 continuation line 2, indented via 4 spaces" <<
|
||||
"bullet 3" <<
|
||||
"continuation paragraph 3, indented via tab" <<
|
||||
"bullet 3.1" <<
|
||||
"continuation paragraph 3.1, indented via 4 spaces" <<
|
||||
"bullet 3.2 continuation line, indented via 2 tabs" <<
|
||||
"bullet 4" <<
|
||||
"continuation paragraph 4, indented via 4 spaces and continuing onto another line too" <<
|
||||
"bullet 5" <<
|
||||
// indenting by only 2 spaces is perhaps non-standard but currently is OK
|
||||
"continuation paragraph 5, indented via 2 spaces and continuing onto another line too" <<
|
||||
"bullet 6" <<
|
||||
"plain old paragraph at the end";
|
||||
|
||||
QFile f(QFINDTESTDATA("data/headingBulletsContinuations.md"));
|
||||
QVERIFY(f.open(QFile::ReadOnly | QIODevice::Text));
|
||||
QString md = QString::fromUtf8(f.readAll());
|
||||
f.close();
|
||||
|
||||
QTextDocument doc;
|
||||
QTextMarkdownImporter(QTextMarkdownImporter::DialectGitHub).import(&doc, md);
|
||||
QTextFrame::iterator iterator = doc.rootFrame()->begin();
|
||||
QTextFrame *currentFrame = iterator.currentFrame();
|
||||
QStringList::const_iterator expectedIt = expectedBlocks.constBegin();
|
||||
int i = 0;
|
||||
while (!iterator.atEnd()) {
|
||||
// There are no child frames
|
||||
QCOMPARE(iterator.currentFrame(), currentFrame);
|
||||
// Check whether we got the right child block
|
||||
QTextBlock block = iterator.currentBlock();
|
||||
QCOMPARE(block.text().contains(LineBreak), false);
|
||||
QCOMPARE(block.text().contains(Tab), false);
|
||||
QVERIFY(!block.text().startsWith(Space));
|
||||
int expectedIndentation = 0;
|
||||
if (block.text().contains(QLatin1String("continuation paragraph")))
|
||||
expectedIndentation = (block.text().contains(Period) ? 2 : 1);
|
||||
qCDebug(lcTests) << i << "child block" << block.text() << "indentation" << block.blockFormat().indent();
|
||||
QVERIFY(expectedIt != expectedBlocks.constEnd());
|
||||
QCOMPARE(block.text(), *expectedIt);
|
||||
if (i > 2)
|
||||
QCOMPARE(block.blockFormat().indent(), expectedIndentation);
|
||||
++iterator;
|
||||
++expectedIt;
|
||||
++i;
|
||||
}
|
||||
QCOMPARE(expectedIt, expectedBlocks.constEnd());
|
||||
|
||||
#ifdef DEBUG_WRITE_HTML
|
||||
{
|
||||
QFile out("/tmp/headingBulletsContinuations.html");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(doc.toHtml().toLatin1());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::thematicBreaks()
|
||||
{
|
||||
int horizontalRuleCount = 0;
|
||||
int textLinesCount = 0;
|
||||
|
||||
QFile f(QFINDTESTDATA("data/thematicBreaks.md"));
|
||||
QVERIFY(f.open(QFile::ReadOnly | QIODevice::Text));
|
||||
QString md = QString::fromUtf8(f.readAll());
|
||||
f.close();
|
||||
|
||||
QTextDocument doc;
|
||||
QTextMarkdownImporter(QTextMarkdownImporter::DialectGitHub).import(&doc, md);
|
||||
QTextFrame::iterator iterator = doc.rootFrame()->begin();
|
||||
QTextFrame *currentFrame = iterator.currentFrame();
|
||||
int i = 0;
|
||||
while (!iterator.atEnd()) {
|
||||
// There are no child frames
|
||||
QCOMPARE(iterator.currentFrame(), currentFrame);
|
||||
// Check whether the block is text or a horizontal rule
|
||||
QTextBlock block = iterator.currentBlock();
|
||||
if (block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth))
|
||||
++horizontalRuleCount;
|
||||
else if (!block.text().isEmpty())
|
||||
++textLinesCount;
|
||||
qCDebug(lcTests) << i << (block.blockFormat().hasProperty(QTextFormat::BlockTrailingHorizontalRulerWidth) ? QLatin1String("- - -") : block.text());
|
||||
++iterator;
|
||||
++i;
|
||||
}
|
||||
QCOMPARE(horizontalRuleCount, 5);
|
||||
QCOMPARE(textLinesCount, 9);
|
||||
|
||||
#ifdef DEBUG_WRITE_HTML
|
||||
{
|
||||
QFile out("/tmp/thematicBreaks.html");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(doc.toHtml().toLatin1());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::lists_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<int>("expectedItemCount");
|
||||
QTest::addColumn<bool>("expectedEmptyItems");
|
||||
QTest::addColumn<QString>("rewrite");
|
||||
|
||||
// Some of these cases show odd behavior, which is subject to change
|
||||
// as the importer and the writer are tweaked to fix bugs over time.
|
||||
QTest::newRow("dot newline") << ".\n" << 0 << true << ".\n\n";
|
||||
QTest::newRow("number dot newline") << "1.\n" << 1 << true << "1. \n";
|
||||
QTest::newRow("star newline") << "*\n" << 1 << true << "* \n";
|
||||
QTest::newRow("hyphen newline") << "-\n" << 1 << true << "- \n";
|
||||
QTest::newRow("hyphen space newline") << "- \n" << 1 << true << "- \n";
|
||||
QTest::newRow("hyphen space letter newline") << "- a\n" << 1 << false << "- a\n";
|
||||
QTest::newRow("hyphen nbsp newline") <<
|
||||
QString::fromUtf8("-\u00A0\n") << 0 << true << "-\u00A0\n\n";
|
||||
QTest::newRow("nested empty lists") << "*\n *\n *\n" << 1 << true << " * \n";
|
||||
QTest::newRow("list nested in empty list") << "-\n * a\n" << 2 << false << "- \n * a\n";
|
||||
QTest::newRow("lists nested in empty lists")
|
||||
<< "-\n * a\n * b\n- c\n *\n + d\n" << 5 << false
|
||||
<< "- \n * a\n * b\n- c *\n + d\n";
|
||||
QTest::newRow("numeric lists nested in empty lists")
|
||||
<< "- \n 1. a\n 2. b\n- c\n 1.\n + d\n" << 4 << false
|
||||
<< "- \n 1. a\n 2. b\n- c 1. + d\n";
|
||||
QTest::newRow("styled spans in list items")
|
||||
<< "1. normal text\n2. **bold** text\n3. `code` in the item\n4. *italic* text\n5. _underlined_ text\n" << 5 << false
|
||||
<< "1. normal text\n2. **bold** text\n3. `code` in the item\n4. *italic* text\n5. _underlined_ text\n";
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::lists()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(int, expectedItemCount);
|
||||
QFETCH(bool, expectedEmptyItems);
|
||||
QFETCH(QString, rewrite);
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setMarkdown(input); // QTBUG-78870 : don't crash
|
||||
|
||||
#ifdef DEBUG_WRITE_HTML
|
||||
{
|
||||
QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".html");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(doc.toHtml().toLatin1());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
QTextFrame::iterator iterator = doc.rootFrame()->begin();
|
||||
QTextFrame *currentFrame = iterator.currentFrame();
|
||||
int i = 0;
|
||||
int itemCount = 0;
|
||||
bool emptyItems = true;
|
||||
QString firstItemFontFamily;
|
||||
while (!iterator.atEnd()) {
|
||||
// There are no child frames
|
||||
QCOMPARE(iterator.currentFrame(), currentFrame);
|
||||
// Check whether the block is text or a horizontal rule
|
||||
QTextBlock block = iterator.currentBlock();
|
||||
if (block.textList()) {
|
||||
++itemCount;
|
||||
if (!block.text().isEmpty())
|
||||
emptyItems = false;
|
||||
}
|
||||
qCDebug(lcTests, "%d %s%s", i,
|
||||
(block.textList() ? "<li>" : "<p>"), qPrintable(block.text()));
|
||||
QTextCharFormat listItemFmt = block.charFormat();
|
||||
QFont listItemFont = listItemFmt.font();
|
||||
// QTextDocumentLayoutPrivate::drawListItem() uses listItemFont to render numbers in an ordered list.
|
||||
// We want that to be consistent, regardless whether the list item's text begins with a styled span.
|
||||
if (firstItemFontFamily.isEmpty())
|
||||
firstItemFontFamily = listItemFont.family();
|
||||
else
|
||||
QCOMPARE(listItemFont.family(), firstItemFontFamily);
|
||||
QCOMPARE(listItemFont.bold(), false);
|
||||
QCOMPARE(listItemFont.italic(), false);
|
||||
QCOMPARE(listItemFont.underline(), false);
|
||||
QCOMPARE(listItemFont.fixedPitch(), false);
|
||||
QCOMPARE(listItemFmt.fontItalic(), false);
|
||||
QCOMPARE(listItemFmt.fontUnderline(), false);
|
||||
QCOMPARE(listItemFmt.fontFixedPitch(), false);
|
||||
++iterator;
|
||||
++i;
|
||||
}
|
||||
QCOMPARE(itemCount, expectedItemCount);
|
||||
QCOMPARE(emptyItems, expectedEmptyItems);
|
||||
if (doc.toMarkdown() != rewrite && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(doc.toMarkdown(), rewrite);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::nestedSpans_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<int>("wordToCheck");
|
||||
QTest::addColumn<CharFormats>("expectedFormat");
|
||||
|
||||
QTest::newRow("bold italic")
|
||||
<< "before ***bold italic*** after"
|
||||
<< 1 << (Bold | Italic);
|
||||
QTest::newRow("bold underlined")
|
||||
<< "before **_bold underlined_** after"
|
||||
<< 1 << (Bold | Underlined);
|
||||
QTest::newRow("italic underlined")
|
||||
<< "before *_italic underlined_* after"
|
||||
<< 1 << (Italic | Underlined);
|
||||
QTest::newRow("bold strikeout")
|
||||
<< "before **~~bold strikeout~~** after"
|
||||
<< 1 << (Bold | Strikeout);
|
||||
QTest::newRow("italic strikeout")
|
||||
<< "before *~~italic strikeout~~* after"
|
||||
<< 1 << (Italic | Strikeout);
|
||||
QTest::newRow("bold italic strikeout")
|
||||
<< "before ***~~bold italic strikeout~~*** after"
|
||||
<< 1 << (Bold | Italic | Strikeout);
|
||||
QTest::newRow("bold link text")
|
||||
<< "before [**bold link**](https://qt.io) after"
|
||||
<< 1 << (Bold | Link);
|
||||
QTest::newRow("italic link text")
|
||||
<< "before [*italic link*](https://qt.io) after"
|
||||
<< 1 << (Italic | Link);
|
||||
QTest::newRow("bold italic link text")
|
||||
<< "before [***bold italic link***](https://qt.io) after"
|
||||
<< 1 << (Bold | Italic | Link);
|
||||
QTest::newRow("strikeout link text")
|
||||
<< "before [~~strikeout link~~](https://qt.io) after"
|
||||
<< 1 << (Strikeout | Link);
|
||||
QTest::newRow("strikeout bold italic link text")
|
||||
<< "before [~~***strikeout bold italic link***~~](https://qt.io) after"
|
||||
<< 1 << (Strikeout | Bold | Italic | Link);
|
||||
QTest::newRow("bold image alt")
|
||||
<< "before [**bold image alt**](/path/to/image.png) after"
|
||||
<< 1 << (Bold | Link);
|
||||
QTest::newRow("bold strikeout italic image alt")
|
||||
<< "before [**~~*bold strikeout italic image alt*~~**](/path/to/image.png) after"
|
||||
<< 1 << (Strikeout | Bold | Italic | Link);
|
||||
// code spans currently override all surrounding formatting
|
||||
QTest::newRow("code in italic span")
|
||||
<< "before *italic `code` and* after"
|
||||
<< 2 << (Mono | Normal);
|
||||
// but the format after the code span ends should revert to what it was before
|
||||
QTest::newRow("code in italic strikeout bold span")
|
||||
<< "before *italic ~~strikeout **bold `code` and**~~* after"
|
||||
<< 5 << (Bold | Italic | Strikeout);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::nestedSpans()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(int, wordToCheck);
|
||||
QFETCH(CharFormats, expectedFormat);
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setMarkdown(input);
|
||||
|
||||
#ifdef DEBUG_WRITE_HTML
|
||||
{
|
||||
QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".html");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(doc.toHtml().toLatin1());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
QTextFrame::iterator iterator = doc.rootFrame()->begin();
|
||||
QTextFrame *currentFrame = iterator.currentFrame();
|
||||
while (!iterator.atEnd()) {
|
||||
// There are no child frames
|
||||
QCOMPARE(iterator.currentFrame(), currentFrame);
|
||||
// Check the QTextCharFormat of the specified word
|
||||
QTextCursor cur(iterator.currentBlock());
|
||||
cur.movePosition(QTextCursor::NextWord, QTextCursor::MoveAnchor, wordToCheck);
|
||||
cur.select(QTextCursor::WordUnderCursor);
|
||||
QTextCharFormat fmt = cur.charFormat();
|
||||
qCDebug(lcTests) << "word" << wordToCheck << cur.selectedText() << "font" << fmt.font()
|
||||
<< "weight" << fmt.fontWeight() << "italic" << fmt.fontItalic()
|
||||
<< "underlined" << fmt.fontUnderline()
|
||||
<< "strikeout" << fmt.fontStrikeOut() << "anchor" << fmt.isAnchor()
|
||||
<< "monospace" << QFontInfo(fmt.font()).fixedPitch() // depends on installed fonts (QTBUG-75649)
|
||||
<< fmt.fontFixedPitch()
|
||||
<< fmt.hasProperty(QTextFormat::FontFixedPitch)
|
||||
<< "expected" << expectedFormat;
|
||||
QCOMPARE(fmt.fontWeight() > QFont::Normal, expectedFormat.testFlag(Bold));
|
||||
QCOMPARE(fmt.fontItalic(), expectedFormat.testFlag(Italic));
|
||||
QCOMPARE(fmt.fontUnderline(), expectedFormat.testFlag(Underlined));
|
||||
QCOMPARE(fmt.fontStrikeOut(), expectedFormat.testFlag(Strikeout));
|
||||
QCOMPARE(fmt.isAnchor(), expectedFormat.testFlag(Link));
|
||||
QCOMPARE(fmt.fontFixedPitch(), expectedFormat.testFlag(Mono));
|
||||
QCOMPARE(fmt.hasProperty(QTextFormat::FontFixedPitch), expectedFormat.testFlag(Mono));
|
||||
++iterator;
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::avoidBlankLineAtBeginning_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<int>("expectedNumberOfParagraphs");
|
||||
|
||||
QTest::newRow("Text block") << QString("Markdown text") << 1;
|
||||
QTest::newRow("Headline") << QString("Markdown text\n============") << 1;
|
||||
QTest::newRow("Code block") << QString(" Markdown text") << 1;
|
||||
QTest::newRow("Unordered list") << QString("* Markdown text") << 1;
|
||||
QTest::newRow("Ordered list") << QString("1. Markdown text") << 1;
|
||||
QTest::newRow("Blockquote") << QString("> Markdown text") << 1;
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::avoidBlankLineAtBeginning() // QTBUG-81060
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(int, expectedNumberOfParagraphs);
|
||||
|
||||
QTextDocument doc;
|
||||
QTextMarkdownImporter(QTextMarkdownImporter::DialectGitHub).import(&doc, input);
|
||||
QTextFrame::iterator iterator = doc.rootFrame()->begin();
|
||||
int i = 0;
|
||||
while (!iterator.atEnd()) {
|
||||
QTextBlock block = iterator.currentBlock();
|
||||
// Make sure there is no empty paragraph at the beginning of the document
|
||||
if (i == 0)
|
||||
QVERIFY(!block.text().isEmpty());
|
||||
++iterator;
|
||||
++i;
|
||||
}
|
||||
QCOMPARE(i, expectedNumberOfParagraphs);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::fragmentsAndProperties_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<int>("fragmentToCheck");
|
||||
QTest::addColumn<QString>("expectedText");
|
||||
QTest::addColumn<QTextFormat::Property>("propertyToCheck");
|
||||
QTest::addColumn<QVariant>("expectedPropertyValue");
|
||||
QTest::addColumn<int>("expectedNumberOfBlocks");
|
||||
QTest::addColumn<int>("expectedNumberOfFragments");
|
||||
|
||||
QTest::newRow("entitiesInHtmlFontBlock") // QTBUG-94245
|
||||
<< QString("<font color='red'><123 test></font> test")
|
||||
<< 0 << "<123 test>" << QTextFormat::ForegroundBrush << QVariant(QBrush(QColor("red")))
|
||||
<< 1 << 2;
|
||||
QTest::newRow("entitiesInHtmlBoldBlock") // QTBUG-91222
|
||||
<< QString("<b>x&lt;</b>")
|
||||
<< 0 << "x<" << QTextFormat::FontWeight << QVariant(700)
|
||||
<< 1 << 1;
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::fragmentsAndProperties()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(int, fragmentToCheck);
|
||||
QFETCH(QString, expectedText);
|
||||
QFETCH(QTextFormat::Property, propertyToCheck);
|
||||
QFETCH(QVariant, expectedPropertyValue);
|
||||
QFETCH(int, expectedNumberOfBlocks);
|
||||
QFETCH(int, expectedNumberOfFragments);
|
||||
|
||||
QTextDocument doc;
|
||||
QTextMarkdownImporter(QTextMarkdownImporter::DialectGitHub).import(&doc, input);
|
||||
#ifdef DEBUG_WRITE_HTML
|
||||
{
|
||||
QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".html");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(doc.toHtml().toLatin1());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
QTextFrame::iterator blockIter = doc.rootFrame()->begin();
|
||||
int blockCount = 0;
|
||||
int fragCount = 0;
|
||||
while (!blockIter.atEnd()) {
|
||||
QTextBlock block = blockIter.currentBlock();
|
||||
auto fragIter = block.begin();
|
||||
while (!fragIter.atEnd()) {
|
||||
auto frag = fragIter.fragment();
|
||||
qCDebug(lcTests) << "fragment" << fragCount << ':' << frag.text() << Qt::hex << frag.charFormat().properties();
|
||||
if (fragCount == fragmentToCheck) {
|
||||
QVariant prop = frag.charFormat().property(propertyToCheck);
|
||||
QCOMPARE(prop, expectedPropertyValue);
|
||||
QCOMPARE(frag.text(), expectedText);
|
||||
}
|
||||
++fragIter;
|
||||
++fragCount;
|
||||
}
|
||||
++blockIter;
|
||||
++blockCount;
|
||||
}
|
||||
QCOMPARE(blockCount, expectedNumberOfBlocks);
|
||||
QCOMPARE(fragCount, expectedNumberOfFragments);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::pathological_data()
|
||||
{
|
||||
QTest::addColumn<QString>("warning");
|
||||
QTest::newRow("fuzz20450") << "attempted to insert into a list that no longer exists";
|
||||
QTest::newRow("fuzz20580") << "";
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::pathological() // avoid crashing on crazy input
|
||||
{
|
||||
QFETCH(QString, warning);
|
||||
QString filename = QLatin1String("data/") + QTest::currentDataTag() + QLatin1String(".md");
|
||||
QFile f(QFINDTESTDATA(filename));
|
||||
QVERIFY(f.open(QFile::ReadOnly));
|
||||
#ifdef QT_NO_DEBUG
|
||||
Q_UNUSED(warning);
|
||||
#else
|
||||
if (!warning.isEmpty())
|
||||
QTest::ignoreMessage(QtWarningMsg, warning.toLatin1());
|
||||
#endif
|
||||
QTextDocument().setMarkdown(f.readAll());
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::fencedCodeBlocks_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<int>("expectedCodeBlockCount");
|
||||
QTest::addColumn<int>("expectedPlainBlockCount");
|
||||
QTest::addColumn<QString>("expectedLanguage");
|
||||
QTest::addColumn<QString>("expectedFenceChar");
|
||||
QTest::addColumn<QString>("rewrite");
|
||||
|
||||
QTest::newRow("backtick fence with language")
|
||||
<< "```pseudocode\nprint('hello world\\n')\n```\n"
|
||||
<< 1 << 0 << "pseudocode" << "`"
|
||||
<< "```pseudocode\nprint('hello world\\n')\n```\n\n";
|
||||
QTest::newRow("tilde fence with language")
|
||||
<< "~~~pseudocode\nprint('hello world\\n')\n~~~\n"
|
||||
<< 1 << 0 << "pseudocode" << "~"
|
||||
<< "~~~pseudocode\nprint('hello world\\n')\n~~~\n\n";
|
||||
QTest::newRow("embedded backticks")
|
||||
<< "```\nnone `one` ``two``\n```\nplain\n```\n```three``` ````four````\n```\nplain\n"
|
||||
<< 2 << 2 << QString() << "`"
|
||||
<< "```\nnone `one` ``two``\n```\nplain\n\n```\n```three``` ````four````\n```\nplain\n\n";
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownImporter::fencedCodeBlocks()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(int, expectedCodeBlockCount);
|
||||
QFETCH(int, expectedPlainBlockCount);
|
||||
QFETCH(QString, expectedLanguage);
|
||||
QFETCH(QString, expectedFenceChar);
|
||||
QFETCH(QString, rewrite);
|
||||
|
||||
QTextDocument doc;
|
||||
doc.setMarkdown(input);
|
||||
|
||||
#ifdef DEBUG_WRITE_HTML
|
||||
{
|
||||
QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".html");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(doc.toHtml().toLatin1());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
QTextFrame::iterator iterator = doc.rootFrame()->begin();
|
||||
QTextFrame *currentFrame = iterator.currentFrame();
|
||||
int codeBlockCount = 0;
|
||||
int plainBlockCount = 0;
|
||||
while (!iterator.atEnd()) {
|
||||
// There are no child frames
|
||||
QCOMPARE(iterator.currentFrame(), currentFrame);
|
||||
// Check whether the block is code or plain
|
||||
QTextBlock block = iterator.currentBlock();
|
||||
const bool codeBlock = block.blockFormat().hasProperty(QTextFormat::BlockCodeFence);
|
||||
QCOMPARE(block.blockFormat().nonBreakableLines(), codeBlock);
|
||||
QCOMPARE(block.blockFormat().stringProperty(QTextFormat::BlockCodeLanguage), codeBlock ? expectedLanguage : QString());
|
||||
if (codeBlock) {
|
||||
QCOMPARE(block.blockFormat().stringProperty(QTextFormat::BlockCodeFence), expectedFenceChar);
|
||||
++codeBlockCount;
|
||||
} else {
|
||||
++plainBlockCount;
|
||||
}
|
||||
qCDebug(lcTests) << (codeBlock ? "code" : "text") << block.text() << block.charFormat().fontFamilies();
|
||||
++iterator;
|
||||
}
|
||||
QCOMPARE(codeBlockCount, expectedCodeBlockCount);
|
||||
QCOMPARE(plainBlockCount, expectedPlainBlockCount);
|
||||
if (doc.toMarkdown() != rewrite && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(doc.toMarkdown(), rewrite);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextMarkdownImporter)
|
||||
#include "tst_qtextmarkdownimporter.moc"
|
20
tests/auto/gui/text/qtextmarkdownwriter/CMakeLists.txt
Normal file
20
tests/auto/gui/text/qtextmarkdownwriter/CMakeLists.txt
Normal file
@ -0,0 +1,20 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextmarkdownwriter Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
list(APPEND test_data "data/example.md")
|
||||
list(APPEND test_data "data/blockquotes.md")
|
||||
|
||||
qt_internal_add_test(tst_qtextmarkdownwriter
|
||||
SOURCES
|
||||
tst_qtextmarkdownwriter.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
62
tests/auto/gui/text/qtextmarkdownwriter/data/blockquotes.md
Normal file
62
tests/auto/gui/text/qtextmarkdownwriter/data/blockquotes.md
Normal file
@ -0,0 +1,62 @@
|
||||
In 1958, Mahatma Gandhi was quoted as follows:
|
||||
|
||||
> The Earth provides enough to satisfy every man's need but not for every man's
|
||||
> greed.
|
||||
|
||||
In [The CommonMark Specification](https://spec.commonmark.org/0.29/) John
|
||||
MacFarlane writes:
|
||||
|
||||
> What distinguishes Markdown from many other lightweight markup syntaxes,
|
||||
> which are often easier to write, is its readability. As Gruber writes:
|
||||
|
||||
> > The overriding design goal for Markdown's formatting syntax is to make it
|
||||
> > as readable as possible. The idea is that a Markdown-formatted document should
|
||||
> > be publishable as-is, as plain text, without looking like it's been marked up
|
||||
> > with tags or formatting instructions. (
|
||||
> > <http://daringfireball.net/projects/markdown/> )
|
||||
|
||||
> The point can be illustrated by comparing a sample of AsciiDoc with an
|
||||
> equivalent sample of Markdown. Here is a sample of AsciiDoc from the AsciiDoc
|
||||
> manual:
|
||||
|
||||
> ```AsciiDoc
|
||||
> 1. List item one.
|
||||
> +
|
||||
> List item one continued with a second paragraph followed by an
|
||||
> Indented block.
|
||||
> +
|
||||
> .................
|
||||
> $ ls *.sh
|
||||
> $ mv *.sh ~/tmp
|
||||
> .................
|
||||
> +
|
||||
> List item continued with a third paragraph.
|
||||
>
|
||||
> 2. List item two continued with an open block.
|
||||
> ...
|
||||
> ```
|
||||
The quotation includes an embedded quotation and a code quotation and ends with
|
||||
an ellipsis due to being incomplete.
|
||||
|
||||
Now let's have an indented code block:
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
int main(void)
|
||||
{
|
||||
printf("# hello markdown\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
and end with a fenced code block:
|
||||
|
||||
~~~pseudocode
|
||||
#include <something.h>
|
||||
#include <else.h>
|
||||
|
||||
a block {
|
||||
a statement;
|
||||
another statement;
|
||||
}
|
||||
~~~
|
||||
|
96
tests/auto/gui/text/qtextmarkdownwriter/data/example.md
Normal file
96
tests/auto/gui/text/qtextmarkdownwriter/data/example.md
Normal file
@ -0,0 +1,96 @@
|
||||
# QTextEdit
|
||||
|
||||
The QTextEdit widget is an advanced editor that supports formatted rich text.
|
||||
It can be used to display HTML and other rich document formats. Internally,
|
||||
QTextEdit uses the QTextDocument class to describe both the high-level
|
||||
structure of each document and the low-level formatting of paragraphs.
|
||||
|
||||
If you are viewing this document in the textedit example, you can edit this
|
||||
document to explore Qt's rich text editing features. We have included some
|
||||
comments in each of the following sections to encourage you to experiment.
|
||||
|
||||
## Font and Paragraph Styles
|
||||
|
||||
QTextEdit supports **bold**, *italic*, _underline_ and ~~strikethrough~~ font
|
||||
styles, and can display multicolored text. Font families such as Times New
|
||||
Roman and `Courier` can also be used directly. *If you place the cursor in a
|
||||
region of styled text, the controls in the tool bars will change to reflect the
|
||||
current style.*
|
||||
|
||||
Paragraphs can be formatted so that the text is left-aligned, right-aligned,
|
||||
centered, or fully justified.
|
||||
|
||||
*Try changing the alignment of some text and resize the editor to see how the
|
||||
text layout changes.*
|
||||
|
||||
## Lists
|
||||
|
||||
Different kinds of lists can be included in rich text documents. Standard
|
||||
bullet lists can be nested, using different symbols for each level of the list:
|
||||
|
||||
- Disc symbols are typically used for top-level list items.
|
||||
* Circle symbols can be used to distinguish between items in lower-level
|
||||
lists.
|
||||
+ Square symbols provide a reasonable alternative to discs and circles.
|
||||
|
||||
Ordered lists can be created that can be used for tables of contents. Different
|
||||
characters can be used to enumerate items, and we can use both Roman and Arabic
|
||||
numerals in the same list structure:
|
||||
|
||||
1. Introduction
|
||||
2. Qt Tools
|
||||
1) Qt Assistant
|
||||
2) Qt Designer
|
||||
1. Form Editor
|
||||
2. Component Architecture
|
||||
3) Qt Linguist
|
||||
|
||||
The list will automatically be renumbered if you add or remove items. *Try
|
||||
adding new sections to the above list or removing existing item to see the
|
||||
numbers change.*
|
||||
|
||||
## Images
|
||||
|
||||
Inline images are treated like ordinary ranges of characters in the text
|
||||
editor, so they flow with the surrounding text. Images can also be selected in
|
||||
the same way as text, making it easy to cut, copy, and paste them.
|
||||
|
||||
 *Try to select this image by clicking and dragging
|
||||
over it with the mouse, or use the text cursor to select it by holding down
|
||||
Shift and using the arrow keys. You can then cut or copy it, and paste it into
|
||||
different parts of this document.*
|
||||
|
||||
## Tables
|
||||
|
||||
QTextEdit can arrange and format tables, supporting features such as row and
|
||||
column spans, text formatting within cells, and size constraints for columns.
|
||||
|
||||
|
||||
| |Development Tools |Programming Techniques |Graphical User Interfaces|
|
||||
|-------------|------------------------------------|---------------------------|-------------------------|
|
||||
|9:00 - 11:00 |Introduction to Qt |||
|
||||
|11:00 - 13:00|Using qmake |Object-oriented Programming|Layouts in Qt |
|
||||
|13:00 - 15:00|Qt Designer Tutorial |Extreme Programming |Writing Custom Styles |
|
||||
|15:00 - 17:00|Qt Linguist and Internationalization|Test-Driven Development | |
|
||||
|
||||
*Try adding text to the cells in the table and experiment with the alignment of
|
||||
the paragraphs.*
|
||||
|
||||
## Hyperlinks
|
||||
|
||||
QTextEdit is designed to support hyperlinks between documents, and this feature
|
||||
is used extensively in
|
||||
[Qt Assistant](http://doc.qt.io/qt-5/qtassistant-index.html). Hyperlinks are
|
||||
automatically created when an HTML file is imported into an editor. Since the
|
||||
rich text framework supports hyperlinks natively, they can also be created
|
||||
programmatically.
|
||||
|
||||
## Undo and Redo
|
||||
|
||||
Full support for undo and redo operations is built into QTextEdit and the
|
||||
underlying rich text framework. Operations on a document can be packaged
|
||||
together to make editing a more comfortable experience for the user.
|
||||
|
||||
*Try making changes to this document and press `Ctrl+Z` to undo them. You can
|
||||
always recover the original contents of the document.*
|
||||
|
@ -0,0 +1,12 @@
|
||||
# heading 1
|
||||
|
||||
- list item 1
|
||||
- list item 2
|
||||
|
||||
## heading 2
|
||||
|
||||
1) list item 1
|
||||
2) list item 2
|
||||
|
||||
the end paragraph
|
||||
|
28
tests/auto/gui/text/qtextmarkdownwriter/data/links.md
Normal file
28
tests/auto/gui/text/qtextmarkdownwriter/data/links.md
Normal file
@ -0,0 +1,28 @@
|
||||
A series of links.
|
||||
|
||||
[link](/uri)
|
||||
|
||||
[link]()
|
||||
|
||||
[link](/uri "title")
|
||||
|
||||
[link](/uri "àbcdè")
|
||||
|
||||
[link](/uri "title title \" title title")
|
||||
|
||||
[link](/url "title \""")
|
||||
|
||||
[link](/url "title
|
||||
title
|
||||
title title
|
||||
\"title\" title \"
|
||||
title")
|
||||
|
||||
* [link](/url "title")
|
||||
* [link](/url)
|
||||
* [link](/url "title
|
||||
title title")
|
||||
* nonlink
|
||||
|
||||
Qt has the <https://qt.io> site
|
||||
|
@ -0,0 +1,31 @@
|
||||
- something happens in the debugger like this:
|
||||
|
||||
```
|
||||
1 QQuickEventPoint::setGrabberItem qquickevents.cpp 869 0x7ffff7a963f2
|
||||
2 QQuickItem::grabMouse qquickitem.cpp 7599 0x7ffff7abea29
|
||||
3 QQuickWindowPrivate::deliverMatchingPointsToItem qquickwindow.cpp 2738 0x7ffff7aea34c
|
||||
4 QQuickWindowPrivate::deliverPressOrReleaseEvent qquickwindow.cpp 2692 0x7ffff7ae9e57
|
||||
5 QQuickWindowPrivate::deliverMouseEvent qquickwindow.cpp 1911 0x7ffff7ae561b
|
||||
6 QQuickWindowPrivate::deliverPointerEvent qquickwindow.cpp 2454 0x7ffff7ae888c
|
||||
7 QQuickWindowPrivate::handleMouseEvent qquickwindow.cpp 2282 0x7ffff7ae7f1a
|
||||
8 QQuickWindow::mousePressEvent qquickwindow.cpp 2249 0x7ffff7ae7bf5
|
||||
9 QQuickView::mousePressEvent qquickview.cpp 626 0x7ffff7bd6bad
|
||||
10 QWindow::event qwindow.cpp 2258 0x7ffff70b2c54
|
||||
```
|
||||
and then I want to explain something about it.
|
||||
|
||||
- something I tried to fix it:
|
||||
|
||||
```c++
|
||||
item->ungrab();
|
||||
```
|
||||
- still didn't fix it, expecting a breakthrough any day now
|
||||
- some sort of miracle
|
||||
- profit!
|
||||
- Alternatively we can have a non-indented fenced code block under a list item:
|
||||
|
||||
```qml
|
||||
import QtQuick
|
||||
Text { text: "hello world" }
|
||||
```
|
||||
- but that means the code block is not part of the list item.
|
13
tests/auto/gui/text/qtextmarkdownwriter/data/wordWrap.md
Normal file
13
tests/auto/gui/text/qtextmarkdownwriter/data/wordWrap.md
Normal file
@ -0,0 +1,13 @@
|
||||
[The CommonMark Specification](https://spec.commonmark.org/0.29/) is the
|
||||
conservative formal specification of the Markdown format, while
|
||||
[GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown)
|
||||
adds extra features such as task lists and tables.
|
||||
|
||||
Qt owes thanks to the authors of the [MD4C parser](https://github.com/mity/md4c)
|
||||
for making markdown import possible. The QTextMarkdownWriter class does not
|
||||
have such dependencies, and also has not yet been tested as extensively, so we
|
||||
do not yet guarantee that we are able to rewrite every Markdown document that
|
||||
you are able to read and display with Text or QTextEdit. But you are free to
|
||||
write [bugs](https://bugreports.qt.io) about any troublesome cases that you
|
||||
encounter.
|
||||
|
@ -0,0 +1,533 @@
|
||||
// Copyright (C) 2019 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#include <QTextDocument>
|
||||
#include <QTextCursor>
|
||||
#include <QTextBlock>
|
||||
#include <QTextList>
|
||||
#include <QTextTable>
|
||||
#include <QBuffer>
|
||||
#include <QDebug>
|
||||
#include <QFontInfo>
|
||||
#include <QLoggingCategory>
|
||||
|
||||
#include <private/qtextmarkdownwriter_p.h>
|
||||
|
||||
Q_LOGGING_CATEGORY(lcTests, "qt.text.tests")
|
||||
|
||||
// #define DEBUG_WRITE_OUTPUT
|
||||
|
||||
class tst_QTextMarkdownWriter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
private slots:
|
||||
void testWriteParagraph_data();
|
||||
void testWriteParagraph();
|
||||
void testWriteList();
|
||||
void testWriteEmptyList();
|
||||
void testWriteCheckboxListItemEndingWithCode();
|
||||
void testWriteNestedBulletLists_data();
|
||||
void testWriteNestedBulletLists();
|
||||
void testWriteNestedNumericLists();
|
||||
void testWriteTable();
|
||||
void rewriteDocument_data();
|
||||
void rewriteDocument();
|
||||
void fromHtml_data();
|
||||
void fromHtml();
|
||||
|
||||
private:
|
||||
bool isMainFontFixed();
|
||||
bool isFixedFontProportional();
|
||||
QString documentToUnixMarkdown();
|
||||
|
||||
private:
|
||||
QTextDocument *document;
|
||||
};
|
||||
|
||||
void tst_QTextMarkdownWriter::init()
|
||||
{
|
||||
document = new QTextDocument();
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::cleanup()
|
||||
{
|
||||
delete document;
|
||||
}
|
||||
|
||||
bool tst_QTextMarkdownWriter::isMainFontFixed()
|
||||
{
|
||||
bool ret = QFontInfo(QGuiApplication::font()).fixedPitch();
|
||||
if (ret) {
|
||||
qCWarning(lcTests) << "QFontDatabase::GeneralFont is monospaced: markdown writing is likely to use too many backticks"
|
||||
<< QFontDatabase::systemFont(QFontDatabase::GeneralFont);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool tst_QTextMarkdownWriter::isFixedFontProportional()
|
||||
{
|
||||
bool ret = !QFontInfo(QFontDatabase::systemFont(QFontDatabase::FixedFont)).fixedPitch();
|
||||
if (ret) {
|
||||
qCWarning(lcTests) << "QFontDatabase::FixedFont is NOT monospaced: markdown writing is likely to use too few backticks"
|
||||
<< QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString tst_QTextMarkdownWriter::documentToUnixMarkdown()
|
||||
{
|
||||
QString ret;
|
||||
QTextStream ts(&ret, QIODevice::WriteOnly);
|
||||
QTextMarkdownWriter writer(ts, QTextDocument::MarkdownDialectGitHub);
|
||||
writer.writeAll(document);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteParagraph_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("expectedOutput");
|
||||
|
||||
QTest::newRow("empty") << "" <<
|
||||
"";
|
||||
QTest::newRow("spaces") << "foobar word" <<
|
||||
"foobar word\n\n";
|
||||
QTest::newRow("starting spaces") << " starting spaces" <<
|
||||
" starting spaces\n\n";
|
||||
QTest::newRow("trailing spaces") << "trailing spaces " <<
|
||||
"trailing spaces \n\n";
|
||||
QTest::newRow("tab") << "word\ttab x" <<
|
||||
"word\ttab x\n\n";
|
||||
QTest::newRow("tab2") << "word\t\ttab\tx" <<
|
||||
"word\t\ttab\tx\n\n";
|
||||
QTest::newRow("misc") << "foobar word\ttab x" <<
|
||||
"foobar word\ttab x\n\n";
|
||||
QTest::newRow("misc2") << "\t \tFoo" <<
|
||||
"\t \tFoo\n\n";
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteParagraph()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, expectedOutput);
|
||||
|
||||
QTextCursor cursor(document);
|
||||
cursor.insertText(input);
|
||||
|
||||
const QString output = documentToUnixMarkdown();
|
||||
if (output != expectedOutput && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(output, expectedOutput);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteList()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
QTextList *list = cursor.createList(QTextListFormat::ListDisc);
|
||||
cursor.insertText("ListItem 1");
|
||||
list->add(cursor.block());
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("ListItem 2");
|
||||
list->add(cursor.block());
|
||||
|
||||
const QString output = documentToUnixMarkdown();
|
||||
const QString expected = QString::fromLatin1("- ListItem 1\n- ListItem 2\n");
|
||||
if (output != expected && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(output, expected);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteEmptyList()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
cursor.createList(QTextListFormat::ListDisc);
|
||||
|
||||
QCOMPARE(documentToUnixMarkdown(), QString::fromLatin1("- \n"));
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteCheckboxListItemEndingWithCode()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
QTextList *list = cursor.createList(QTextListFormat::ListDisc);
|
||||
cursor.insertText("Image.originalSize property (not necessary; PdfDocument.pagePointSize() substitutes)");
|
||||
list->add(cursor.block());
|
||||
{
|
||||
auto fmt = cursor.block().blockFormat();
|
||||
fmt.setMarker(QTextBlockFormat::MarkerType::Unchecked);
|
||||
cursor.setBlockFormat(fmt);
|
||||
}
|
||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 2);
|
||||
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor);
|
||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor, 4);
|
||||
QCOMPARE(cursor.selectedText(), QString::fromLatin1("PdfDocument.pagePointSize()"));
|
||||
auto fmt = cursor.charFormat();
|
||||
fmt.setFontFixedPitch(true);
|
||||
cursor.setCharFormat(fmt);
|
||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::MoveAnchor, 5);
|
||||
cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor);
|
||||
cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor, 4);
|
||||
QCOMPARE(cursor.selectedText(), QString::fromLatin1("Image.originalSize"));
|
||||
cursor.setCharFormat(fmt);
|
||||
|
||||
const QString output = documentToUnixMarkdown();
|
||||
const QString expected = QString::fromLatin1(
|
||||
"- [ ] `Image.originalSize` property (not necessary; `PdfDocument.pagePointSize()`\n substitutes)\n");
|
||||
if (output != expected && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(output, expected);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteNestedBulletLists_data()
|
||||
{
|
||||
QTest::addColumn<bool>("checkbox");
|
||||
QTest::addColumn<bool>("checked");
|
||||
QTest::addColumn<bool>("continuationLine");
|
||||
QTest::addColumn<bool>("continuationParagraph");
|
||||
QTest::addColumn<QString>("expectedOutput");
|
||||
|
||||
QTest::newRow("plain bullets") << false << false << false << false <<
|
||||
"- ListItem 1\n * ListItem 2\n + ListItem 3\n- ListItem 4\n * ListItem 5\n";
|
||||
QTest::newRow("bullets with continuation lines") << false << false << true << false <<
|
||||
"- ListItem 1\n * ListItem 2\n + ListItem 3 with text that won't fit on one line and thus needs a\n continuation\n- ListItem 4\n * ListItem 5 with text that won't fit on one line and thus needs a\n continuation\n";
|
||||
QTest::newRow("bullets with continuation paragraphs") << false << false << false << true <<
|
||||
"- ListItem 1\n\n * ListItem 2\n + ListItem 3\n\n continuation\n\n- ListItem 4\n\n * ListItem 5\n\n continuation\n\n";
|
||||
QTest::newRow("unchecked") << true << false << false << false <<
|
||||
"- [ ] ListItem 1\n * [ ] ListItem 2\n + [ ] ListItem 3\n- [ ] ListItem 4\n * [ ] ListItem 5\n";
|
||||
QTest::newRow("checked") << true << true << false << false <<
|
||||
"- [x] ListItem 1\n * [x] ListItem 2\n + [x] ListItem 3\n- [x] ListItem 4\n * [x] ListItem 5\n";
|
||||
QTest::newRow("checked with continuation lines") << true << true << true << false <<
|
||||
"- [x] ListItem 1\n * [x] ListItem 2\n + [x] ListItem 3 with text that won't fit on one line and thus needs a\n continuation\n- [x] ListItem 4\n * [x] ListItem 5 with text that won't fit on one line and thus needs a\n continuation\n";
|
||||
QTest::newRow("checked with continuation paragraphs") << true << true << false << true <<
|
||||
"- [x] ListItem 1\n\n * [x] ListItem 2\n + [x] ListItem 3\n\n continuation\n\n- [x] ListItem 4\n\n * [x] ListItem 5\n\n continuation\n\n";
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteNestedBulletLists()
|
||||
{
|
||||
QFETCH(bool, checkbox);
|
||||
QFETCH(bool, checked);
|
||||
QFETCH(bool, continuationParagraph);
|
||||
QFETCH(bool, continuationLine);
|
||||
QFETCH(QString, expectedOutput);
|
||||
|
||||
QTextCursor cursor(document);
|
||||
QTextBlockFormat blockFmt = cursor.blockFormat();
|
||||
if (checkbox) {
|
||||
blockFmt.setMarker(checked ? QTextBlockFormat::MarkerType::Checked : QTextBlockFormat::MarkerType::Unchecked);
|
||||
cursor.setBlockFormat(blockFmt);
|
||||
}
|
||||
|
||||
QTextList *list1 = cursor.createList(QTextListFormat::ListDisc);
|
||||
cursor.insertText("ListItem 1");
|
||||
list1->add(cursor.block());
|
||||
|
||||
QTextListFormat fmt2;
|
||||
fmt2.setStyle(QTextListFormat::ListCircle);
|
||||
fmt2.setIndent(2);
|
||||
QTextList *list2 = cursor.insertList(fmt2);
|
||||
cursor.insertText("ListItem 2");
|
||||
|
||||
QTextListFormat fmt3;
|
||||
fmt3.setStyle(QTextListFormat::ListSquare);
|
||||
fmt3.setIndent(3);
|
||||
cursor.insertList(fmt3);
|
||||
cursor.insertText(continuationLine ?
|
||||
"ListItem 3 with text that won't fit on one line and thus needs a continuation" :
|
||||
"ListItem 3");
|
||||
if (continuationParagraph) {
|
||||
QTextBlockFormat blockFmt;
|
||||
blockFmt.setIndent(2);
|
||||
cursor.insertBlock(blockFmt);
|
||||
cursor.insertText("continuation");
|
||||
}
|
||||
|
||||
cursor.insertBlock(blockFmt);
|
||||
cursor.insertText("ListItem 4");
|
||||
list1->add(cursor.block());
|
||||
|
||||
cursor.insertBlock();
|
||||
cursor.insertText(continuationLine ?
|
||||
"ListItem 5 with text that won't fit on one line and thus needs a continuation" :
|
||||
"ListItem 5");
|
||||
list2->add(cursor.block());
|
||||
if (continuationParagraph) {
|
||||
QTextBlockFormat blockFmt;
|
||||
blockFmt.setIndent(2);
|
||||
cursor.insertBlock(blockFmt);
|
||||
cursor.insertText("continuation");
|
||||
}
|
||||
|
||||
const QString output = documentToUnixMarkdown();
|
||||
#ifdef DEBUG_WRITE_OUTPUT
|
||||
{
|
||||
QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".md");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(output.toUtf8());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
if (output != expectedOutput && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(output, expectedOutput);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteNestedNumericLists()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
|
||||
QTextList *list1 = cursor.createList(QTextListFormat::ListDecimal);
|
||||
cursor.insertText("ListItem 1");
|
||||
list1->add(cursor.block());
|
||||
|
||||
QTextListFormat fmt2;
|
||||
fmt2.setStyle(QTextListFormat::ListLowerAlpha);
|
||||
fmt2.setNumberSuffix(QLatin1String(")"));
|
||||
fmt2.setIndent(2);
|
||||
QTextList *list2 = cursor.insertList(fmt2);
|
||||
cursor.insertText("ListItem 2");
|
||||
|
||||
QTextListFormat fmt3;
|
||||
fmt3.setStyle(QTextListFormat::ListDecimal);
|
||||
fmt3.setIndent(3);
|
||||
cursor.insertList(fmt3);
|
||||
cursor.insertText("ListItem 3");
|
||||
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("ListItem 4");
|
||||
list1->add(cursor.block());
|
||||
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("ListItem 5");
|
||||
list2->add(cursor.block());
|
||||
|
||||
const QString output = documentToUnixMarkdown();
|
||||
// There's no QTextList API to set the starting number so we hard-coded all lists to start at 1 (QTBUG-65384)
|
||||
const QString expected = QString::fromLatin1(
|
||||
"1. ListItem 1\n 1) ListItem 2\n 1. ListItem 3\n2. ListItem 4\n 2) ListItem 5\n");
|
||||
if (output != expected && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(output, expected);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::testWriteTable()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
QTextTable * table = cursor.insertTable(4, 3);
|
||||
cursor = table->cellAt(0, 0).firstCursorPosition();
|
||||
// valid Markdown tables need headers, but QTextTable doesn't make that distinction
|
||||
// so QTextMarkdownWriter assumes the first row of any table is a header
|
||||
cursor.insertText("one");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("two");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("three");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
|
||||
cursor.insertText("alice");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("bob");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("carl");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
|
||||
cursor.insertText("dennis");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("eric");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("fiona");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
|
||||
cursor.insertText("gina");
|
||||
/*
|
||||
|one |two |three|
|
||||
|------|----|-----|
|
||||
|alice |bob |carl |
|
||||
|dennis|eric|fiona|
|
||||
|gina | | |
|
||||
*/
|
||||
|
||||
QString md = documentToUnixMarkdown();
|
||||
|
||||
#ifdef DEBUG_WRITE_OUTPUT
|
||||
{
|
||||
QFile out("/tmp/table.md");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(md.toUtf8());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
QString expected = QString::fromLatin1(
|
||||
"\n|one |two |three|\n|------|----|-----|\n|alice |bob |carl |\n|dennis|eric|fiona|\n|gina | | |\n\n");
|
||||
if (md != expected && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(md, expected);
|
||||
|
||||
// create table with merged cells
|
||||
document->clear();
|
||||
cursor = QTextCursor(document);
|
||||
table = cursor.insertTable(3, 3);
|
||||
table->mergeCells(0, 0, 1, 2);
|
||||
table->mergeCells(1, 1, 1, 2);
|
||||
cursor = table->cellAt(0, 0).firstCursorPosition();
|
||||
cursor.insertText("a");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("b");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("c");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("d");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("e");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("f");
|
||||
/*
|
||||
+---+-+
|
||||
|a |b|
|
||||
+---+-+
|
||||
|c| d|
|
||||
+-+-+-+
|
||||
|e|f| |
|
||||
+-+-+-+
|
||||
|
||||
generates
|
||||
|
||||
|a ||b|
|
||||
|-|-|-|
|
||||
|c|d ||
|
||||
|e|f| |
|
||||
|
||||
*/
|
||||
|
||||
md = documentToUnixMarkdown();
|
||||
|
||||
#ifdef DEBUG_WRITE_OUTPUT
|
||||
{
|
||||
QFile out("/tmp/table-merged-cells.md");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(md.toUtf8());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
expected = QString::fromLatin1("\n|a ||b|\n|-|-|-|\n|c|d ||\n|e|f| |\n\n");
|
||||
if (md != expected && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(md, expected);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::rewriteDocument_data()
|
||||
{
|
||||
QTest::addColumn<QString>("inputFile");
|
||||
|
||||
QTest::newRow("block quotes") << "blockquotes.md";
|
||||
QTest::newRow("example") << "example.md";
|
||||
QTest::newRow("list items after headings") << "headingsAndLists.md";
|
||||
QTest::newRow("word wrap") << "wordWrap.md";
|
||||
QTest::newRow("links") << "links.md";
|
||||
QTest::newRow("lists and code blocks") << "listsAndCodeBlocks.md";
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::rewriteDocument()
|
||||
{
|
||||
QFETCH(QString, inputFile);
|
||||
QTextDocument doc;
|
||||
QFile f(QFINDTESTDATA("data/" + inputFile));
|
||||
QVERIFY(f.open(QFile::ReadOnly | QIODevice::Text));
|
||||
QString orig = QString::fromUtf8(f.readAll());
|
||||
f.close();
|
||||
doc.setMarkdown(orig);
|
||||
QString md = doc.toMarkdown();
|
||||
|
||||
#ifdef DEBUG_WRITE_OUTPUT
|
||||
QFile out("/tmp/rewrite-" + inputFile);
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(md.toUtf8());
|
||||
out.close();
|
||||
#endif
|
||||
|
||||
if (md != orig && isMainFontFixed())
|
||||
QEXPECT_FAIL("", "fixed-pitch main font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(md, orig);
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::fromHtml_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("expectedOutput");
|
||||
|
||||
QTest::newRow("long URL") <<
|
||||
"<span style=\"font-style:italic;\">https://www.example.com/dir/subdir/subsubdir/subsubsubdir/subsubsubsubdir/subsubsubsubsubdir/</span>" <<
|
||||
"*https://www.example.com/dir/subdir/subsubdir/subsubsubdir/subsubsubsubdir/subsubsubsubsubdir/*\n\n";
|
||||
QTest::newRow("non-emphasis inline asterisk") << "3 * 4" << "3 * 4\n\n";
|
||||
QTest::newRow("arithmetic") << "(2 * a * x + b)^2 = b^2 - 4 * a * c" << "(2 * a * x + b)^2 = b^2 - 4 * a * c\n\n";
|
||||
QTest::newRow("escaped asterisk after newline") <<
|
||||
"The first sentence of this paragraph holds 80 characters, then there's a star. * This is wrapped, but is <em>not</em> a bullet point." <<
|
||||
"The first sentence of this paragraph holds 80 characters, then there's a star.\n\\* This is wrapped, but is *not* a bullet point.\n\n";
|
||||
QTest::newRow("escaped plus after newline") <<
|
||||
"The first sentence of this paragraph holds 80 characters, then there's a plus. + This is wrapped, but is <em>not</em> a bullet point." <<
|
||||
"The first sentence of this paragraph holds 80 characters, then there's a plus.\n\\+ This is wrapped, but is *not* a bullet point.\n\n";
|
||||
QTest::newRow("escaped hyphen after newline") <<
|
||||
"The first sentence of this paragraph holds 80 characters, then there's a minus. - This is wrapped, but is <em>not</em> a bullet point." <<
|
||||
"The first sentence of this paragraph holds 80 characters, then there's a minus.\n\\- This is wrapped, but is *not* a bullet point.\n\n";
|
||||
QTest::newRow("list items with indented continuations") <<
|
||||
"<ul><li>bullet<p>continuation paragraph</p></li><li>another bullet<br/>continuation line</li></ul>" <<
|
||||
"- bullet\n\n continuation paragraph\n\n- another bullet\n continuation line\n";
|
||||
QTest::newRow("nested list items with continuations") <<
|
||||
"<ul><li>bullet<p>continuation paragraph</p></li><li>another bullet<br/>continuation line</li><ul><li>bullet<p>continuation paragraph</p></li><li>another bullet<br/>continuation line</li></ul></ul>" <<
|
||||
"- bullet\n\n continuation paragraph\n\n- another bullet\n continuation line\n\n - bullet\n\n continuation paragraph\n\n - another bullet\n continuation line\n";
|
||||
QTest::newRow("nested ordered list items with continuations") <<
|
||||
"<ol><li>item<p>continuation paragraph</p></li><li>another item<br/>continuation line</li><ol><li>item<p>continuation paragraph</p></li><li>another item<br/>continuation line</li></ol><li>another</li><li>another</li></ol>" <<
|
||||
"1. item\n\n continuation paragraph\n\n2. another item\n continuation line\n\n 1. item\n\n continuation paragraph\n\n 2. another item\n continuation line\n\n3. another\n4. another\n";
|
||||
QTest::newRow("thematic break") <<
|
||||
"something<hr/>something else" <<
|
||||
"something\n\n- - -\nsomething else\n\n";
|
||||
QTest::newRow("block quote") <<
|
||||
"<p>In 1958, Mahatma Gandhi was quoted as follows:</p><blockquote>The Earth provides enough to satisfy every man's need but not for every man's greed.</blockquote>" <<
|
||||
"In 1958, Mahatma Gandhi was quoted as follows:\n\n> The Earth provides enough to satisfy every man's need but not for every man's\n> greed.\n\n";
|
||||
QTest::newRow("image") <<
|
||||
"<img src=\"/url\" alt=\"foo\" title=\"title\"/>" <<
|
||||
"\n\n";
|
||||
QTest::newRow("code") <<
|
||||
"<pre class=\"language-pseudocode\">\n#include \"foo.h\"\n\nblock {\n statement();\n}\n\n</pre>" <<
|
||||
"```pseudocode\n#include \"foo.h\"\n\nblock {\n statement();\n}\n\n```\n\n";
|
||||
// TODO
|
||||
// QTest::newRow("escaped number and paren after double newline") <<
|
||||
// "<p>(The first sentence of this paragraph is a line, the next paragraph has a number</p>13) but that's not part of an ordered list" <<
|
||||
// "(The first sentence of this paragraph is a line, the next paragraph has a number\n\n13\\) but that's not part of an ordered list\n\n";
|
||||
QTest::newRow("preformats with embedded backticks") <<
|
||||
"<pre>none `one` ``two``</pre>plain<pre>```three``` ````four````</pre>plain" <<
|
||||
"```\nnone `one` ``two``\n\n```\nplain\n\n```\n```three``` ````four````\n\n```\nplain\n\n";
|
||||
QTest::newRow("list items with and without checkboxes") <<
|
||||
"<ul><li>bullet</li><li class=\"unchecked\">unchecked item</li><li class=\"checked\">checked item</li></ul>" <<
|
||||
"- bullet\n- [ ] unchecked item\n- [x] checked item\n";
|
||||
}
|
||||
|
||||
void tst_QTextMarkdownWriter::fromHtml()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, expectedOutput);
|
||||
|
||||
document->setHtml(input);
|
||||
QString output = documentToUnixMarkdown();
|
||||
|
||||
#ifdef DEBUG_WRITE_OUTPUT
|
||||
{
|
||||
QFile out("/tmp/" + QLatin1String(QTest::currentDataTag()) + ".md");
|
||||
out.open(QFile::WriteOnly);
|
||||
out.write(output.toUtf8());
|
||||
out.close();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (output != expectedOutput && (isMainFontFixed() || isFixedFontProportional()))
|
||||
QEXPECT_FAIL("", "fixed main font or proportional fixed font (QTBUG-103484)", Continue);
|
||||
QCOMPARE(output, expectedOutput);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextMarkdownWriter)
|
||||
#include "tst_qtextmarkdownwriter.moc"
|
21
tests/auto/gui/text/qtextobject/CMakeLists.txt
Normal file
21
tests/auto/gui/text/qtextobject/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextobject Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextobject
|
||||
SOURCES
|
||||
tst_qtextobject.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qtextobject CONDITION TARGET Qt::Widgets
|
||||
LIBRARIES
|
||||
Qt::Widgets
|
||||
)
|
93
tests/auto/gui/text/qtextobject/tst_qtextobject.cpp
Normal file
93
tests/auto/gui/text/qtextobject/tst_qtextobject.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QTest>
|
||||
|
||||
#include <qcoreapplication.h>
|
||||
#include <qdebug.h>
|
||||
#include <qtextobject.h>
|
||||
#include <qtextdocument.h>
|
||||
#ifndef QT_NO_WIDGETS
|
||||
#include <qtextedit.h>
|
||||
#endif
|
||||
#include <qtextcursor.h>
|
||||
|
||||
class tst_QTextObject : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
tst_QTextObject();
|
||||
virtual ~tst_QTextObject();
|
||||
|
||||
private slots:
|
||||
#ifndef QT_NO_WIDGETS
|
||||
void getSetCheck();
|
||||
#endif
|
||||
void testStandAloneTextObject();
|
||||
};
|
||||
|
||||
tst_QTextObject::tst_QTextObject()
|
||||
{
|
||||
}
|
||||
|
||||
tst_QTextObject::~tst_QTextObject()
|
||||
{
|
||||
}
|
||||
|
||||
#ifndef QT_NO_WIDGETS
|
||||
// Testing get/set functions
|
||||
void tst_QTextObject::getSetCheck()
|
||||
{
|
||||
QTextEdit edit;
|
||||
QTextFrame obj1(edit.document());
|
||||
// QTextFrameLayoutData * QTextFrame::layoutData()
|
||||
// void QTextFrame::setLayoutData(QTextFrameLayoutData *)
|
||||
QTextFrameLayoutData *var1 = new QTextFrameLayoutData;
|
||||
obj1.setLayoutData(var1);
|
||||
QCOMPARE(var1, obj1.layoutData());
|
||||
obj1.setLayoutData((QTextFrameLayoutData *)0);
|
||||
QCOMPARE((QTextFrameLayoutData *)0, obj1.layoutData());
|
||||
// delete var1; // No delete, since QTextFrame takes ownership
|
||||
|
||||
QTextBlock obj2 = edit.textCursor().block();
|
||||
// QTextBlockUserData * QTextBlock::userData()
|
||||
// void QTextBlock::setUserData(QTextBlockUserData *)
|
||||
QTextBlockUserData *var2 = new QTextBlockUserData;
|
||||
obj2.setUserData(var2);
|
||||
QCOMPARE(var2, obj2.userData());
|
||||
obj2.setUserData((QTextBlockUserData *)0);
|
||||
QCOMPARE((QTextBlockUserData *)0, obj2.userData());
|
||||
|
||||
// int QTextBlock::userState()
|
||||
// void QTextBlock::setUserState(int)
|
||||
obj2.setUserState(0);
|
||||
QCOMPARE(0, obj2.userState());
|
||||
obj2.setUserState(INT_MIN);
|
||||
QCOMPARE(INT_MIN, obj2.userState());
|
||||
obj2.setUserState(INT_MAX);
|
||||
QCOMPARE(INT_MAX, obj2.userState());
|
||||
}
|
||||
#endif
|
||||
|
||||
class TestTextObject : public QTextObject
|
||||
{
|
||||
public:
|
||||
TestTextObject(QTextDocument *document) : QTextObject(document) {}
|
||||
};
|
||||
|
||||
void tst_QTextObject::testStandAloneTextObject()
|
||||
{
|
||||
QTextDocument document;
|
||||
TestTextObject textObject(&document);
|
||||
|
||||
QCOMPARE(textObject.document(), &document);
|
||||
// don't crash
|
||||
textObject.format();
|
||||
textObject.formatIndex();
|
||||
QCOMPARE(textObject.objectIndex(), -1);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextObject)
|
||||
#include "tst_qtextobject.moc"
|
15
tests/auto/gui/text/qtextodfwriter/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qtextodfwriter/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextodfwriter Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextodfwriter
|
||||
SOURCES
|
||||
tst_qtextodfwriter.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
404
tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp
Normal file
404
tests/auto/gui/text/qtextodfwriter/tst_qtextodfwriter.cpp
Normal file
@ -0,0 +1,404 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <QTest>
|
||||
#include <QTextDocument>
|
||||
#include <QTextCursor>
|
||||
#include <QTextBlock>
|
||||
#include <QTextList>
|
||||
#include <QTextTable>
|
||||
#include <QBuffer>
|
||||
#include <QDebug>
|
||||
|
||||
#include <private/qtextodfwriter_p.h>
|
||||
|
||||
class tst_QTextOdfWriter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
|
||||
private slots:
|
||||
void testWriteParagraph_data();
|
||||
void testWriteParagraph();
|
||||
void testWriteStyle1_data();
|
||||
void testWriteStyle1();
|
||||
void testWriteStyle2();
|
||||
void testWriteList();
|
||||
void testWriteList2();
|
||||
void createArchive();
|
||||
void testWriteAll();
|
||||
void testWriteSection();
|
||||
void testWriteTable();
|
||||
void testWriteFrameFormat();
|
||||
|
||||
private:
|
||||
/// closes the document and returns the part of the XML stream that the test wrote
|
||||
QString getContentFromXml();
|
||||
|
||||
private:
|
||||
QTextDocument *document;
|
||||
QXmlStreamWriter *xmlWriter;
|
||||
QTextOdfWriter *odfWriter;
|
||||
QBuffer *buffer;
|
||||
};
|
||||
|
||||
void tst_QTextOdfWriter::init()
|
||||
{
|
||||
document = new QTextDocument();
|
||||
odfWriter = new QTextOdfWriter(*document, 0);
|
||||
|
||||
buffer = new QBuffer();
|
||||
buffer->open(QIODevice::WriteOnly);
|
||||
xmlWriter = new QXmlStreamWriter(buffer);
|
||||
xmlWriter->writeNamespace(odfWriter->officeNS, "office");
|
||||
xmlWriter->writeNamespace(odfWriter->textNS, "text");
|
||||
xmlWriter->writeNamespace(odfWriter->styleNS, "style");
|
||||
xmlWriter->writeNamespace(odfWriter->foNS, "fo");
|
||||
xmlWriter->writeNamespace(odfWriter->tableNS, "table");
|
||||
xmlWriter->writeStartDocument();
|
||||
xmlWriter->writeStartElement("dummy");
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::cleanup()
|
||||
{
|
||||
delete document;
|
||||
delete odfWriter;
|
||||
delete xmlWriter;
|
||||
delete buffer;
|
||||
}
|
||||
|
||||
QString tst_QTextOdfWriter::getContentFromXml()
|
||||
{
|
||||
xmlWriter->writeEndDocument();
|
||||
buffer->close();
|
||||
QString stringContent = QString::fromUtf8(buffer->data());
|
||||
QString ret;
|
||||
int index = stringContent.indexOf("<dummy");
|
||||
if (index > 0) {
|
||||
index = stringContent.indexOf('>', index);
|
||||
if (index > 0)
|
||||
ret = stringContent.mid(index+1, stringContent.size() - index - 10);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteParagraph_data()
|
||||
{
|
||||
QTest::addColumn<QString>("input");
|
||||
QTest::addColumn<QString>("xml");
|
||||
|
||||
QTest::newRow("empty") << "" <<
|
||||
"<text:p text:style-name=\"p1\"/>";
|
||||
QTest::newRow("spaces") << "foobar word" <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foobar <text:s text:c=\"2\"/>word</text:span></text:p>";
|
||||
QTest::newRow("starting spaces") << " starting spaces" <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\"><text:s text:c=\"2\"/>starting spaces</text:span></text:p>";
|
||||
QTest::newRow("trailing spaces") << "trailing spaces " <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">trailing spaces <text:s/></text:span></text:p>";
|
||||
QTest::newRow("tab") << "word\ttab x" <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">word<text:tab/>tab x</text:span></text:p>";
|
||||
QTest::newRow("tab2") << "word\t\ttab\tx" <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">word<text:tab/><text:tab/>tab<text:tab/>x</text:span></text:p>";
|
||||
QTest::newRow("misc") << "foobar word\ttab x" <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foobar <text:s text:c=\"2\"/>word<text:tab/>tab x</text:span></text:p>";
|
||||
QTest::newRow("misc2") << "\t \tFoo" <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\"><text:tab/> <text:s text:c=\"4\"/><text:tab/>Foo</text:span></text:p>";
|
||||
QTest::newRow("linefeed") << (QStringLiteral("line1") + QChar(0x2028) + QStringLiteral("line2")) <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">line1<text:tab/><text:line-break/>line2</text:span></text:p>";
|
||||
QTest::newRow("spaces") << "The quick brown fox jumped over the lazy dog" <<
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">The quick brown fox jumped over the lazy dog</text:span></text:p>";
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteParagraph()
|
||||
{
|
||||
QFETCH(QString, input);
|
||||
QFETCH(QString, xml);
|
||||
|
||||
QTextCursor cursor(document);
|
||||
cursor.insertText(input);
|
||||
|
||||
odfWriter->writeBlock(*xmlWriter, document->begin());
|
||||
QCOMPARE( getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteStyle1_data()
|
||||
{
|
||||
QTest::addColumn<QString>("htmlInput");
|
||||
QTest::addColumn<int>("cursorPosition");
|
||||
QTest::addColumn<QString>("xml");
|
||||
|
||||
QString text1 = "Normal<b>bold</b><i>italic</i><b><i>Bold/Italic</i></b>";
|
||||
QTest::newRow("normal") << text1 << 2 <<
|
||||
"<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-family=\"Sans\"/></style:style>";
|
||||
QTest::newRow("bold") << text1 << 10 <<
|
||||
"<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-weight=\"bold\" fo:font-family=\"Sans\"/></style:style>";
|
||||
QTest::newRow("italic") << text1 << 14 <<
|
||||
"<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-style=\"italic\" fo:font-family=\"Sans\"/></style:style>";
|
||||
QTest::newRow("bold+italic") << text1 << 25 <<
|
||||
"<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-style=\"italic\" fo:font-weight=\"bold\" fo:font-family=\"Sans\"/></style:style>";
|
||||
QString colorText = "<span style=\"color: #00FF00; background-color: #FF0000;\"> Color Text </span>";
|
||||
QTest::newRow("green/red") << colorText << 3 <<
|
||||
"<style:style style:name=\"c4\" style:family=\"text\"><style:text-properties fo:font-family=\"Sans\" fo:color=\"#00ff00\" fo:background-color=\"#ff0000\"/></style:style>";
|
||||
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteStyle1()
|
||||
{
|
||||
QFETCH(QString, htmlInput);
|
||||
QFETCH(int, cursorPosition);
|
||||
QFETCH(QString, xml);
|
||||
document->setHtml(htmlInput);
|
||||
|
||||
QTextCursor cursor(document);
|
||||
cursor.setPosition(cursorPosition);
|
||||
odfWriter->writeCharacterFormat(*xmlWriter, cursor.charFormat(), 4);
|
||||
QCOMPARE( getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteStyle2()
|
||||
{
|
||||
QTextBlockFormat bf; // = cursor.blockFormat();
|
||||
QList<QTextOption::Tab> tabs;
|
||||
QTextOption::Tab tab1(40, QTextOption::RightTab);
|
||||
tabs << tab1;
|
||||
QTextOption::Tab tab2(80, QTextOption::DelimiterTab, 'o');
|
||||
tabs << tab2;
|
||||
bf.setTabPositions(tabs);
|
||||
|
||||
odfWriter->writeBlockFormat(*xmlWriter, bf, 1);
|
||||
QString xml = QString::fromLatin1(
|
||||
"<style:style style:name=\"p1\" style:family=\"paragraph\">"
|
||||
"<style:paragraph-properties>"
|
||||
"<style:tab-stops>"
|
||||
"<style:tab-stop style:position=\"30pt\" style:type=\"right\"/>"
|
||||
"<style:tab-stop style:position=\"60pt\" style:type=\"char\" style:char=\"o\"/>"
|
||||
"</style:tab-stops>"
|
||||
"</style:paragraph-properties>"
|
||||
"</style:style>");
|
||||
QCOMPARE(getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteList()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
QTextList *list = cursor.createList(QTextListFormat::ListDisc);
|
||||
cursor.insertText("ListItem 1");
|
||||
list->add(cursor.block());
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("ListItem 2");
|
||||
list->add(cursor.block());
|
||||
|
||||
odfWriter->writeBlock(*xmlWriter, cursor.block());
|
||||
QString xml = QString::fromLatin1(
|
||||
"<text:list text:style-name=\"L2\">"
|
||||
"<text:list-item>"
|
||||
//"<text:numbered-paragraph text:style-name=\"L2\" text:level=\"1\">"
|
||||
//"<text:number>")+ QChar(0x25cf) + QString::fromLatin1("</text:number>" // 0x25cf is a bullet
|
||||
"<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">ListItem 2</text:span></text:p>"
|
||||
"</text:list-item>"
|
||||
"</text:list>");
|
||||
|
||||
QCOMPARE(getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteList2()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
QTextList *list = cursor.createList(QTextListFormat::ListDisc);
|
||||
cursor.insertText("Cars");
|
||||
list->add(cursor.block());
|
||||
cursor.insertBlock();
|
||||
QTextListFormat level2;
|
||||
level2.setStyle(QTextListFormat::ListSquare);
|
||||
level2.setIndent(2);
|
||||
QTextList *list2 = cursor.createList(level2);
|
||||
cursor.insertText("Model T");
|
||||
list2->add(cursor.block());
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("Kitt");
|
||||
list2->add(cursor.block());
|
||||
cursor.insertBlock();
|
||||
cursor.insertText("Animals");
|
||||
list->add(cursor.block());
|
||||
|
||||
cursor.insertBlock(QTextBlockFormat(), QTextCharFormat()); // start a new completely unrelated list.
|
||||
QTextList *list3 = cursor.createList(QTextListFormat::ListDecimal);
|
||||
cursor.insertText("Foo");
|
||||
list3->add(cursor.block());
|
||||
|
||||
// and another block thats NOT in a list.
|
||||
cursor.insertBlock(QTextBlockFormat(), QTextCharFormat());
|
||||
cursor.insertText("Bar");
|
||||
|
||||
odfWriter->writeFrame(*xmlWriter, document->rootFrame());
|
||||
QString xml = QString::fromLatin1(
|
||||
"<text:list text:style-name=\"L2\">"
|
||||
"<text:list-item>"
|
||||
//"<text:numbered-paragraph text:style-name=\"L2\" text:level=\"1\">"
|
||||
//"<text:number>")+ QChar(0x25cf) + QString::fromLatin1("</text:number>" // 0x25cf is a bullet
|
||||
"<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">Cars</text:span></text:p>"
|
||||
"</text:list-item>"
|
||||
"<text:list-item>"
|
||||
"<text:list text:style-name=\"L4\">"
|
||||
"<text:list-item>"
|
||||
"<text:p text:style-name=\"p5\"><text:span text:style-name=\"c0\">Model T</text:span></text:p>"
|
||||
"</text:list-item>"
|
||||
"<text:list-item>"
|
||||
"<text:p text:style-name=\"p5\"><text:span text:style-name=\"c0\">Kitt</text:span></text:p>"
|
||||
"</text:list-item>"
|
||||
"</text:list>"
|
||||
"</text:list-item>"
|
||||
"<text:list-item>"
|
||||
"<text:p text:style-name=\"p3\"><text:span text:style-name=\"c0\">Animals</text:span></text:p>"
|
||||
"</text:list-item>"
|
||||
"</text:list>"
|
||||
"<text:list text:style-name=\"L6\">"
|
||||
"<text:list-item>"
|
||||
"<text:p text:style-name=\"p7\"><text:span text:style-name=\"c0\">Foo</text:span></text:p>"
|
||||
"</text:list-item>"
|
||||
"</text:list>"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">Bar</text:span></text:p>");
|
||||
|
||||
// QString x = getContentFromXml();
|
||||
// for (int i=0; i < x.length(); i+=150) qDebug() << x.mid(i, 150);
|
||||
QCOMPARE(getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
|
||||
void tst_QTextOdfWriter::createArchive()
|
||||
{
|
||||
document->setPlainText("a"); // simple doc is enough ;)
|
||||
QTextOdfWriter writer(*document, buffer);
|
||||
QCOMPARE(writer.createArchive(), true); // default
|
||||
writer.writeAll();
|
||||
/*
|
||||
QFile file("createArchive-odt");
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(buffer->data());
|
||||
file.close();
|
||||
*/
|
||||
QVERIFY(buffer->data().size() > 80);
|
||||
QCOMPARE(buffer->data()[0], 'P'); // its a zip :)
|
||||
QCOMPARE(buffer->data()[1], 'K');
|
||||
QString mimetype(buffer->data().mid(38, 39));
|
||||
QCOMPARE(mimetype, QString::fromLatin1("application/vnd.oasis.opendocument.text"));
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteAll()
|
||||
{
|
||||
document->setPlainText("a"); // simple doc is enough ;)
|
||||
QTextOdfWriter writer(*document, buffer);
|
||||
QCOMPARE(writer.createArchive(), true);
|
||||
writer.setCreateArchive(false);
|
||||
writer.writeAll();
|
||||
QString result = QString(buffer->data());
|
||||
// details we check elsewhere, all we have to do is check availability.
|
||||
QVERIFY(result.indexOf("office:automatic-styles") >= 0);
|
||||
QVERIFY(result.indexOf("<style:style style:name=\"p1\"") >= 0);
|
||||
QVERIFY(result.indexOf("<style:style style:name=\"c0\"") >= 0);
|
||||
QVERIFY(result.indexOf("office:body") >= 0);
|
||||
QVERIFY(result.indexOf("office:text") >= 0);
|
||||
QVERIFY(result.indexOf("style:style") >= 0);
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteSection()
|
||||
{
|
||||
QTextCursor cursor(document);
|
||||
cursor.insertText("foo\nBar");
|
||||
QTextFrameFormat ff;
|
||||
cursor.insertFrame(ff);
|
||||
cursor.insertText("baz");
|
||||
|
||||
odfWriter->writeFrame(*xmlWriter, document->rootFrame());
|
||||
QString xml = QString::fromLatin1(
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">foo</text:span></text:p>"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">Bar</text:span></text:p>"
|
||||
"<text:section>"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">baz</text:span></text:p>"
|
||||
"</text:section>"
|
||||
"<text:p text:style-name=\"p1\"/>");
|
||||
|
||||
QCOMPARE(getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteTable()
|
||||
{
|
||||
// create table with merged cells
|
||||
QTextCursor cursor(document);
|
||||
QTextTable * table = cursor.insertTable(3, 3);
|
||||
table->mergeCells(1, 0, 2, 2);
|
||||
table->mergeCells(0, 1, 1, 2);
|
||||
cursor = table->cellAt(0, 0).firstCursorPosition();
|
||||
cursor.insertText("a");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("b");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("c");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("d");
|
||||
cursor.movePosition(QTextCursor::NextCell);
|
||||
cursor.insertText("e");
|
||||
/*
|
||||
+-+---+
|
||||
|a|b |
|
||||
+-+-+-+
|
||||
|c |d|
|
||||
+ +-+
|
||||
| |e|
|
||||
+-+-+-+
|
||||
*/
|
||||
|
||||
odfWriter->writeFrame(*xmlWriter, document->rootFrame());
|
||||
QString xml = QString::fromLatin1(
|
||||
"<text:p text:style-name=\"p1\"/>"
|
||||
"<table:table table:style-name=\"Table2\">"
|
||||
"<table:table-column table:number-columns-repeated=\"3\"/>"
|
||||
"<table:table-row>"
|
||||
"<table:table-cell table:style-name=\"T3\">"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">a</text:span></text:p>"
|
||||
"</table:table-cell>"
|
||||
"<table:table-cell table:number-columns-spanned=\"2\" table:style-name=\"T6\">"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c7\">b</text:span></text:p>"
|
||||
"</table:table-cell>"
|
||||
"</table:table-row>"
|
||||
"<table:table-row>"
|
||||
"<table:table-cell table:number-columns-spanned=\"2\" table:number-rows-spanned=\"2\" table:style-name=\"T5\">"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c8\">c</text:span></text:p>"
|
||||
"</table:table-cell>"
|
||||
"<table:table-cell table:style-name=\"T3\">"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">d</text:span></text:p>"
|
||||
"</table:table-cell>"
|
||||
"</table:table-row>"
|
||||
"<table:table-row>"
|
||||
"<table:table-cell table:style-name=\"T3\">"
|
||||
"<text:p text:style-name=\"p1\"><text:span text:style-name=\"c0\">e</text:span></text:p>"
|
||||
"</table:table-cell>"
|
||||
"</table:table-row>"
|
||||
"</table:table>"
|
||||
"<text:p text:style-name=\"p1\"/>");
|
||||
|
||||
QCOMPARE(getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
void tst_QTextOdfWriter::testWriteFrameFormat()
|
||||
{
|
||||
QTextFrameFormat tff;
|
||||
tff.setTopMargin(20);
|
||||
tff.setBottomMargin(20);
|
||||
tff.setLeftMargin(20);
|
||||
tff.setRightMargin(20);
|
||||
QTextCursor tc(document);
|
||||
odfWriter->writeFrameFormat(*xmlWriter, tff, 0);
|
||||
// Value of 15pt is based on the pixelToPoint() calculation done in qtextodfwriter.cpp
|
||||
QString xml = QString::fromLatin1(
|
||||
"<style:style style:name=\"s0\" style:family=\"section\">"
|
||||
"<style:section-properties fo:margin-top=\"15pt\" fo:margin-bottom=\"15pt\""
|
||||
" fo:margin-left=\"15pt\" fo:margin-right=\"15pt\"/>"
|
||||
"</style:style>");
|
||||
QCOMPARE(getContentFromXml(), xml);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QTextOdfWriter)
|
||||
#include "tst_qtextodfwriter.moc"
|
23
tests/auto/gui/text/qtextpiecetable/CMakeLists.txt
Normal file
23
tests/auto/gui/text/qtextpiecetable/CMakeLists.txt
Normal file
@ -0,0 +1,23 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
if(WIN32)
|
||||
return()
|
||||
endif()
|
||||
if(NOT QT_FEATURE_private_tests)
|
||||
return()
|
||||
endif()
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextpiecetable Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextpiecetable
|
||||
SOURCES
|
||||
../qtextdocument/common.h
|
||||
tst_qtextpiecetable.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
1115
tests/auto/gui/text/qtextpiecetable/tst_qtextpiecetable.cpp
Normal file
1115
tests/auto/gui/text/qtextpiecetable/tst_qtextpiecetable.cpp
Normal file
File diff suppressed because it is too large
Load Diff
15
tests/auto/gui/text/qtextscriptengine/CMakeLists.txt
Normal file
15
tests/auto/gui/text/qtextscriptengine/CMakeLists.txt
Normal file
@ -0,0 +1,15 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtextscriptengine Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtextscriptengine
|
||||
SOURCES
|
||||
tst_qtextscriptengine.cpp
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
@ -0,0 +1,17 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## generate Binary:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_executable(generate
|
||||
GUI
|
||||
SOURCES
|
||||
main.cpp
|
||||
INCLUDE_DIRECTORIES
|
||||
/usr/include/freetype2
|
||||
LIBRARIES
|
||||
Qt::CorePrivate
|
||||
Qt::Gui
|
||||
)
|
91
tests/auto/gui/text/qtextscriptengine/generate/main.cpp
Normal file
91
tests/auto/gui/text/qtextscriptengine/generate/main.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
|
||||
#include <QApplication>
|
||||
#include <QTextEdit>
|
||||
#include <QLineEdit>
|
||||
#include <QVBoxLayout>
|
||||
#include <QFontDialog>
|
||||
#include <QPushButton>
|
||||
|
||||
#define private public
|
||||
#include <qfont.h>
|
||||
#include <private/qtextengine_p.h>
|
||||
#include <private/qfontengine_p.h>
|
||||
#include <qtextlayout.h>
|
||||
#undef private
|
||||
|
||||
|
||||
class MyEdit : public QTextEdit {
|
||||
Q_OBJECT
|
||||
public:
|
||||
MyEdit(QWidget *p) : QTextEdit(p) { setReadOnly(true); }
|
||||
public slots:
|
||||
void setText(const QString &str);
|
||||
void changeFont();
|
||||
public:
|
||||
QLineEdit *lineEdit;
|
||||
};
|
||||
|
||||
void MyEdit::setText(const QString &str)
|
||||
{
|
||||
if (str.isEmpty()) {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
QTextLayout layout(str, lineEdit->font());
|
||||
QTextEngine *e = layout.d;
|
||||
e->itemize();
|
||||
e->shape(0);
|
||||
|
||||
QString result;
|
||||
result = "Using font '" + e->fontEngine(e->layoutData->items[0])->fontDef.family + "'\n\n";
|
||||
|
||||
result += "{ { ";
|
||||
for (int i = 0; i < str.length(); ++i)
|
||||
result += "0x" + QString::number(str.at(i).unicode(), 16) + ", ";
|
||||
result += "0x0 },\n { ";
|
||||
for (int i = 0; i < e->layoutData->items[0].num_glyphs; ++i)
|
||||
result += "0x" + QString::number(e->layoutData->glyphLayout.glyphs[i], 16) + ", ";
|
||||
result += "0x0 } }";
|
||||
|
||||
setPlainText(result);
|
||||
}
|
||||
|
||||
void MyEdit::changeFont()
|
||||
{
|
||||
bool ok;
|
||||
QFont f = QFontDialog::getFont(&ok, lineEdit->font(), topLevelWidget());
|
||||
if (ok)
|
||||
lineEdit->setFont(f);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
|
||||
QWidget *mw = new QWidget(0);
|
||||
QVBoxLayout *l = new QVBoxLayout(mw);
|
||||
|
||||
QLineEdit *le = new QLineEdit(mw);
|
||||
l->addWidget(le);
|
||||
|
||||
MyEdit *view = new MyEdit(mw);
|
||||
view->lineEdit = le;
|
||||
l->addWidget(view);
|
||||
|
||||
QPushButton *button = new QPushButton("Change Font", mw);
|
||||
l->addWidget(button);
|
||||
|
||||
QObject::connect(le, SIGNAL(textChanged(QString)), view, SLOT(setText(QString)));
|
||||
QObject::connect(button, SIGNAL(clicked()), view, SLOT(changeFont()));
|
||||
|
||||
mw->resize(500, 300);
|
||||
mw->show();
|
||||
|
||||
return a.exec();
|
||||
}
|
||||
|
||||
|
||||
#include <main.moc>
|
1355
tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp
Normal file
1355
tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp
Normal file
File diff suppressed because it is too large
Load Diff
22
tests/auto/gui/text/qtexttable/CMakeLists.txt
Normal file
22
tests/auto/gui/text/qtexttable/CMakeLists.txt
Normal file
@ -0,0 +1,22 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qtexttable Test:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_add_test(tst_qtexttable
|
||||
SOURCES
|
||||
tst_qtexttable.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
)
|
||||
|
||||
## Scopes:
|
||||
#####################################################################
|
||||
|
||||
qt_internal_extend_target(tst_qtexttable CONDITION TARGET Qt::Widgets
|
||||
LIBRARIES
|
||||
Qt::Widgets
|
||||
)
|
1344
tests/auto/gui/text/qtexttable/tst_qtexttable.cpp
Normal file
1344
tests/auto/gui/text/qtexttable/tst_qtexttable.cpp
Normal file
File diff suppressed because it is too large
Load Diff
21
tests/auto/gui/text/qzip/CMakeLists.txt
Normal file
21
tests/auto/gui/text/qzip/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
||||
# Copyright (C) 2022 The Qt Company Ltd.
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
#####################################################################
|
||||
## tst_qzip Test:
|
||||
#####################################################################
|
||||
|
||||
# Collect test data
|
||||
file(GLOB_RECURSE test_data
|
||||
RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
testdata/*
|
||||
)
|
||||
|
||||
qt_internal_add_test(tst_qzip
|
||||
SOURCES
|
||||
tst_qzip.cpp
|
||||
LIBRARIES
|
||||
Qt::Gui
|
||||
Qt::GuiPrivate
|
||||
TESTDATA ${test_data}
|
||||
)
|
BIN
tests/auto/gui/text/qzip/testdata/symlink.zip
vendored
Normal file
BIN
tests/auto/gui/text/qzip/testdata/symlink.zip
vendored
Normal file
Binary file not shown.
BIN
tests/auto/gui/text/qzip/testdata/test.zip
vendored
Normal file
BIN
tests/auto/gui/text/qzip/testdata/test.zip
vendored
Normal file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user