From 0ec07b68ad34e135451dd5291732bf73d297ba0c Mon Sep 17 00:00:00 2001 From: Konstantin Ritt Date: Thu, 10 Apr 2014 13:50:53 +0300 Subject: Improve the Unicode script itemization implementation Make it closer to the Unicode specs (UAX#24): * Common now inherits the preceding character's script, if any; * In a combining character sequence, if the base character is of Common script, the entire sequence is treated like if it were of the first non-Inherited, non-Common script in the sequence. See http://www.unicode.org/reports/tr24/tr24-21.html for more details. [ChangeLog][QtGui] Fixed regression in arabic text rendering. Task-number: QTBUG-28813 Task-number: QTBUG-29930 (related) Task-number: QTBUG-35836 Change-Id: Id85761965b08ca94c674d5f3613fe58b82b2ce9c Reviewed-by: Eskil Abrahamsen Blomfeldt Reviewed-by: Ahmed Saidi --- src/corelib/tools/qunicodetools.cpp | 38 +++++++++++++++++++++++++++++++++---- src/gui/text/qtextengine.cpp | 17 +---------------- 2 files changed, 35 insertions(+), 20 deletions(-) (limited to 'src') diff --git a/src/corelib/tools/qunicodetools.cpp b/src/corelib/tools/qunicodetools.cpp index fac795051a..fc36d07a4a 100644 --- a/src/corelib/tools/qunicodetools.cpp +++ b/src/corelib/tools/qunicodetools.cpp @@ -667,7 +667,7 @@ Q_CORE_EXPORT void initCharAttributes(const ushort *string, int length, // ---------------------------------------------------------------------------- // -// The Unicode script property. See http://www.unicode.org/reports/tr24/ (some very old version) +// The Unicode script property. See http://www.unicode.org/reports/tr24/tr24-21.html // // ---------------------------------------------------------------------------- @@ -689,15 +689,36 @@ Q_CORE_EXPORT void initScripts(const ushort *string, int length, uchar *scripts) const QUnicodeTables::Properties *prop = QUnicodeTables::properties(ucs4); - if (Q_LIKELY(prop->script == script || prop->script == QChar::Script_Inherited)) + if (Q_LIKELY(prop->script == script || prop->script <= QChar::Script_Inherited)) continue; // Never break between a combining mark (gc= Mc, Mn or Me) and its base character. // Thus, a combining mark — whatever its script property value is — should inherit // the script property value of its base character. static const int test = (FLAG(QChar::Mark_NonSpacing) | FLAG(QChar::Mark_SpacingCombining) | FLAG(QChar::Mark_Enclosing)); - if (Q_UNLIKELY(FLAG(prop->category) & test)) - continue; + if (Q_UNLIKELY(FLAG(prop->category) & test)) { + // In cases where the base character itself has the Common script property value, + // and it is followed by one or more combining marks with a specific script property value, + // it may be even better for processing to let the base acquire the script property value + // from the first mark. This approach can be generalized by treating all the characters + // of a combining character sequence as having the script property value + // of the first non-Inherited, non-Common character in the sequence if there is one, + // and otherwise treating all the characters as having the Common script property value. + if (Q_LIKELY(script > QChar::Script_Common || prop->script <= QChar::Script_Common)) + continue; + + script = QChar::Script(prop->script); + } + + if (Q_LIKELY(script != QChar::Script_Common)) { + // override preceding Common-s + while (sor > 0 && scripts[sor - 1] == QChar::Script_Common) + --sor; + } else { + // see if we are inheriting preceding run + if (sor > 0) + script = scripts[sor - 1]; + } while (sor < eor) scripts[sor++] = script; @@ -705,6 +726,15 @@ Q_CORE_EXPORT void initScripts(const ushort *string, int length, uchar *scripts) script = prop->script; } eor = length; + if (Q_LIKELY(script != QChar::Script_Common)) { + // override preceding Common-s + while (sor > 0 && scripts[sor - 1] == QChar::Script_Common) + --sor; + } else { + // see if we are inheriting preceding run + if (sor > 0) + script = scripts[sor - 1]; + } while (sor < eor) scripts[sor++] = script; } diff --git a/src/gui/text/qtextengine.cpp b/src/gui/text/qtextengine.cpp index 967ba24fcf..34788dc4dc 100644 --- a/src/gui/text/qtextengine.cpp +++ b/src/gui/text/qtextengine.cpp @@ -122,20 +122,9 @@ private: return; const int end = start + length; for (int i = start + 1; i < end; ++i) { - // According to the unicode spec we should be treating characters in the Common script - // (punctuation, spaces, etc) as being the same script as the surrounding text for the - // purpose of splitting up text. This is important because, for example, a fullstop - // (0x2E) can be used to indicate an abbreviation and so must be treated as part of a - // word. Thus it must be passed along with the word in languages that have to calculate - // word breaks. For example the thai word "ครม." has no word breaks but the word "ครม" - // does. - // Unfortuntely because we split up the strings for both wordwrapping and for setting - // the font and because Japanese and Chinese are also aliases of the script "Common", - // doing this would break too many things. So instead we only pass the full stop - // along, and nothing else. if (m_analysis[i].bidiLevel == m_analysis[start].bidiLevel && m_analysis[i].flags == m_analysis[start].flags - && (m_analysis[i].script == m_analysis[start].script || m_string[i] == QLatin1Char('.')) + && m_analysis[i].script == m_analysis[start].script && m_analysis[i].flags < QScriptAnalysis::SpaceTabOrObject && i - start < MaxItemLength) continue; @@ -1515,26 +1504,22 @@ void QTextEngine::itemize() const while (uc < e) { switch (*uc) { case QChar::ObjectReplacementCharacter: - analysis->script = QChar::Script_Common; analysis->flags = QScriptAnalysis::Object; break; case QChar::LineSeparator: if (analysis->bidiLevel % 2) --analysis->bidiLevel; - analysis->script = QChar::Script_Common; analysis->flags = QScriptAnalysis::LineOrParagraphSeparator; if (option.flags() & QTextOption::ShowLineAndParagraphSeparators) *const_cast(uc) = 0x21B5; // visual line separator break; case QChar::Tabulation: - analysis->script = QChar::Script_Common; analysis->flags = QScriptAnalysis::Tab; analysis->bidiLevel = control.baseLevel(); break; case QChar::Space: case QChar::Nbsp: if (option.flags() & QTextOption::ShowTabsAndSpaces) { - analysis->script = QChar::Script_Common; analysis->flags = QScriptAnalysis::Space; analysis->bidiLevel = control.baseLevel(); break; -- cgit v1.2.3