QTextOption: Add flag for showing default ignorable characters

The presence of the flag instructs the text shaping engine to not
outright ignore normally non-printable code points, so that if the font
has glyphs for them, they will be rendered. This is useful when editing
plain text whose layout is affected by the presence of such characters
(primarily BiDi control characters), and for layout debugging in
general.

[ChangeLog][QtGui][Text] Added QTextOption::ShowDefaultIgnorables flag.

Change-Id: I610a30603f718254ab8e532083e65252351159f0
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
Reviewed-by: Christian Ehrlicher <ch.ehrlicher@gmx.de>
This commit is contained in:
Igor Khanin 2024-07-12 21:20:01 +03:00
parent 38bb72720a
commit 889bdf1de4
6 changed files with 70 additions and 3 deletions

View File

@ -1498,16 +1498,20 @@ void QTextEngine::shapeText(int item) const
for (int i = 0; i < itemLength; ++i, ++glyph_pos) { for (int i = 0; i < itemLength; ++i, ++glyph_pos) {
log_clusters[i] = glyph_pos; log_clusters[i] = glyph_pos;
initialGlyphs.attributes[glyph_pos].clusterStart = true; initialGlyphs.attributes[glyph_pos].clusterStart = true;
bool is_print_char;
if (QChar::isHighSurrogate(string[i]) if (QChar::isHighSurrogate(string[i])
&& i + 1 < itemLength && i + 1 < itemLength
&& QChar::isLowSurrogate(string[i + 1])) { && QChar::isLowSurrogate(string[i + 1])) {
initialGlyphs.attributes[glyph_pos].dontPrint = !QChar::isPrint(QChar::surrogateToUcs4(string[i], string[i + 1])); is_print_char = QChar::isPrint(QChar::surrogateToUcs4(string[i], string[i + 1]));
++i; ++i;
log_clusters[i] = glyph_pos; log_clusters[i] = glyph_pos;
} else { } else {
initialGlyphs.attributes[glyph_pos].dontPrint = !QChar::isPrint(string[i]); is_print_char = QChar::isPrint(string[i]);
} }
initialGlyphs.attributes[glyph_pos].dontPrint =
!is_print_char && !(option.flags() & QTextOption::ShowDefaultIgnorables);
if (Q_UNLIKELY(!initialGlyphs.attributes[glyph_pos].dontPrint)) { if (Q_UNLIKELY(!initialGlyphs.attributes[glyph_pos].dontPrint)) {
QFontEngine *actualFontEngine = fontEngine; QFontEngine *actualFontEngine = fontEngine;
@ -1638,7 +1642,7 @@ int QTextEngine::shapeTextWithHarfbuzzNG(const QScriptItem &si,
uint buffer_flags = HB_BUFFER_FLAG_DEFAULT; uint buffer_flags = HB_BUFFER_FLAG_DEFAULT;
// Symbol encoding used to encode various crap in the 32..255 character code range, // Symbol encoding used to encode various crap in the 32..255 character code range,
// and thus might override U+00AD [SHY]; avoid hiding default ignorables // and thus might override U+00AD [SHY]; avoid hiding default ignorables
if (Q_UNLIKELY(actualFontEngine->symbol)) if (Q_UNLIKELY(actualFontEngine->symbol || (option.flags() & QTextOption::ShowDefaultIgnorables)))
buffer_flags |= HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES; buffer_flags |= HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES;
hb_buffer_set_flags(buffer, hb_buffer_flags_t(buffer_flags)); hb_buffer_set_flags(buffer, hb_buffer_flags_t(buffer_flags));

View File

@ -268,6 +268,7 @@ QList<QTextOption::Tab> QTextOption::tabs() const
shown differently to breaking spaces. shown differently to breaking spaces.
\value ShowLineAndParagraphSeparators Visualize line and paragraph separators with appropriate symbol characters. \value ShowLineAndParagraphSeparators Visualize line and paragraph separators with appropriate symbol characters.
\value [since 5.7] ShowDocumentTerminator Visualize the end of the document with a section sign. \value [since 5.7] ShowDocumentTerminator Visualize the end of the document with a section sign.
\value [since 6.9] ShowDefaultIgnorables Render normally non-visual characters if supported by font.
\value AddSpaceForLineAndParagraphSeparators While determining the line-break positions take into account the \value AddSpaceForLineAndParagraphSeparators While determining the line-break positions take into account the
space added for drawing a separator character. space added for drawing a separator character.
\value SuppressColors Suppress all color changes in the character formats (except the main selection). \value SuppressColors Suppress all color changes in the character formats (except the main selection).

View File

@ -73,6 +73,7 @@ public:
AddSpaceForLineAndParagraphSeparators = 0x4, AddSpaceForLineAndParagraphSeparators = 0x4,
SuppressColors = 0x8, SuppressColors = 0x8,
ShowDocumentTerminator = 0x10, ShowDocumentTerminator = 0x10,
ShowDefaultIgnorables = 0x20,
IncludeTrailingSpaces = 0x80000000, IncludeTrailingSpaces = 0x80000000,
}; };
Q_DECLARE_FLAGS(Flags, Flag) Q_DECLARE_FLAGS(Flags, Flag)

View File

@ -27,9 +27,13 @@ set_source_files_properties("../../../shared/resources/testfont.ttf"
set_source_files_properties("../../../shared/resources/testfont_linemetrics.otf" set_source_files_properties("../../../shared/resources/testfont_linemetrics.otf"
PROPERTIES QT_RESOURCE_ALIAS "testfont_linemetrics.otf" PROPERTIES QT_RESOURCE_ALIAS "testfont_linemetrics.otf"
) )
set_source_files_properties("../../../shared/resources/testfont_bidimarks.ttf"
PROPERTIES QT_RESOURCE_ALIAS "testfont_bidimarks.ttf"
)
set(testfont_resource_files set(testfont_resource_files
"../../../shared/resources/testfont.ttf" "../../../shared/resources/testfont.ttf"
"../../../shared/resources/testfont_linemetrics.otf" "../../../shared/resources/testfont_linemetrics.otf"
"../../../shared/resources/testfont_bidimarks.ttf"
"ucs4font.ttf" "ucs4font.ttf"
) )

View File

@ -37,6 +37,8 @@ private slots:
void largeText_data(); void largeText_data();
void largeText(); // QTBUG-123339 void largeText(); // QTBUG-123339
void typoLineMetrics(); void typoLineMetrics();
void defaultIgnorableHorizontalAdvance_data();
void defaultIgnorableHorizontalAdvance();
}; };
void tst_QFontMetrics::same() void tst_QFontMetrics::same()
@ -454,5 +456,60 @@ void tst_QFontMetrics::typoLineMetrics()
} }
} }
void tst_QFontMetrics::defaultIgnorableHorizontalAdvance_data()
{
QTest::addColumn<bool>("withShaping");
QTest::newRow("With Text Shaping") << true;
QTest::newRow("Without Text Shaping") << false;
}
void tst_QFontMetrics::defaultIgnorableHorizontalAdvance()
{
QFETCH(bool, withShaping);
// testfont_bidimarks.ttf is a version of testfont.ttf with additional
// glyphs for U+200E (LRM) and U+200F (RLM)
QString testFont = QFINDTESTDATA("fonts/testfont_bidimarks.ttf");
QVERIFY(!testFont.isEmpty());
int id = QFontDatabase::addApplicationFont(testFont);
QVERIFY(id >= 0);
auto cleanup = qScopeGuard([&id] {
if (id >= 0)
QFontDatabase::removeApplicationFont(id);
});
QFont withoutSupport;
QFont withSupport(QFontDatabase::applicationFontFamilies(id).at(0));
if (!withShaping) {
withoutSupport.setStyleStrategy(QFont::PreferNoShaping);
withSupport.setStyleStrategy(QFont::PreferNoShaping);
}
QFontMetrics withoutSupportMetrics(withoutSupport);
QFontMetrics withSupportMetrics(withSupport);
QTextOption opt;
opt.setFlags(QTextOption::ShowDefaultIgnorables);
const QChar LRM = (ushort)0x200e;
const QString str = QStringLiteral("[") + LRM + QStringLiteral("]");
int withoutSupportWithoutIgnorablesAdvance = withoutSupportMetrics.horizontalAdvance(str);
int withoutSupportWithIgnorablesAdvance = withoutSupportMetrics.horizontalAdvance(str, opt);
QCOMPARE_GT(withoutSupportWithoutIgnorablesAdvance, 0);
QCOMPARE_EQ(withoutSupportWithIgnorablesAdvance, withoutSupportWithoutIgnorablesAdvance);
int withSupportWithoutIgnorablesAdvance = withSupportMetrics.horizontalAdvance(str);
int withSupportWithIgnorablesAdvance = withSupportMetrics.horizontalAdvance(str, opt);
QCOMPARE_GT(withSupportWithoutIgnorablesAdvance, 0);
QCOMPARE_GT(withSupportWithIgnorablesAdvance, withSupportWithoutIgnorablesAdvance);
}
QTEST_MAIN(tst_QFontMetrics) QTEST_MAIN(tst_QFontMetrics)
#include "tst_qfontmetrics.moc" #include "tst_qfontmetrics.moc"

Binary file not shown.