summaryrefslogtreecommitdiffstats
path: root/lib/kotext/KoComplexText.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/kotext/KoComplexText.cpp')
-rw-r--r--lib/kotext/KoComplexText.cpp1476
1 files changed, 1476 insertions, 0 deletions
diff --git a/lib/kotext/KoComplexText.cpp b/lib/kotext/KoComplexText.cpp
new file mode 100644
index 000000000..b955b3327
--- /dev/null
+++ b/lib/kotext/KoComplexText.cpp
@@ -0,0 +1,1476 @@
+/****************************************************************************
+** $Id: KoComplexText.cpp 504496 2006-02-01 11:16:49Z dfaure $
+**
+** Implementation of some internal classes
+**
+** Created :
+**
+** Copyright (C) 2001 Trolltech AS. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be distributed under the terms of the Q Public License
+** as defined by Trolltech AS of Norway and appearing in the file
+** LICENSE.QPL included in the packaging of this file.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
+** licenses may use this file in accordance with the Qt Commercial License
+** Agreement provided with the Software.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
+** information about Qt Commercial License Agreements.
+** See http://www.trolltech.com/qpl/ for QPL licensing information.
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+** Contact info@trolltech.com if any conditions of this licensing are
+** not clear to you.
+**
+**********************************************************************/
+
+
+#ifndef QT_NO_COMPLEXTEXT
+#include "KoRichText.h"
+//#include "qfontdata_p.h"
+#include "qfontmetrics.h"
+#include "qrect.h"
+
+#include <stdlib.h>
+
+// -----------------------------------------------------
+
+/* a small helper class used internally to resolve Bidi embedding levels.
+ Each line of text caches the embedding level at the start of the line for faster
+ relayouting
+*/
+KoBidiContext::KoBidiContext( uchar l, QChar::Direction e, KoBidiContext *p, bool o )
+ : level(l) , override(o), dir(e)
+{
+ if ( p )
+ p->ref();
+ parent = p;
+ count = 0;
+}
+
+KoBidiContext::~KoBidiContext()
+{
+ if( parent && parent->deref() )
+ delete parent;
+}
+
+static QChar *shapeBuffer = 0;
+static int shapeBufSize = 0;
+
+/*
+ Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
+ arabic).
+
+ Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
+ transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
+
+ Right join-causing: dual + center
+ Left join-causing: dual + right + center
+
+ Rules are as follows (for a string already in visual order, as we have it here):
+
+ R1 Transparent characters do not affect joining behaviour.
+ R2 A right joining character, that has a right join-causing char on the right will get form XRight
+ (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
+ Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
+ R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
+ the right will get form XMedial
+ R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
+ will get form XRight
+ R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
+ will get form XLeft
+ R7 Otherwise the character will get form XIsolated
+
+ Additionally we have to do the minimal ligature support for lam-alef ligatures:
+
+ L1 Transparent characters do not affect ligature behaviour.
+ L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
+ L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
+
+ The two functions defined in this class do shaping in visual and logical order. For logical order just replace right with
+ previous and left with next in the above rules ;-)
+*/
+
+/*
+ Two small helper functions for arabic shaping. They get the next shape causing character on either
+ side of the char in question. Implements rule R1.
+
+ leftChar() returns true if the char to the left is a left join-causing char
+ rightChar() returns true if the char to the right is a right join-causing char
+*/
+static inline const QChar *prevChar( const QString &str, int pos )
+{
+ //kdDebug() << "leftChar: pos=" << pos << endl;
+ pos--;
+ const QChar *ch = str.unicode() + pos;
+ while( pos > -1 ) {
+ if( !ch->isMark() )
+ return ch;
+ pos--;
+ ch--;
+ }
+ return &QChar::replacement;
+}
+
+static inline const QChar *nextChar( const QString &str, int pos)
+{
+ pos++;
+ int len = str.length();
+ const QChar *ch = str.unicode() + pos;
+ while( pos < len ) {
+ //kdDebug() << "rightChar: " << pos << " isLetter=" << ch.isLetter() << ", joining=" << ch.joining() << endl;
+ if( !ch->isMark() )
+ return ch;
+ // assume it's a transparent char, this might not be 100% correct
+ pos++;
+ ch++;
+ }
+ return &QChar::replacement;
+}
+
+static inline bool prevVisualCharJoins( const QString &str, int pos)
+{
+ return ( prevChar( str, pos )->joining() != QChar::OtherJoining );
+}
+
+static inline bool nextVisualCharJoins( const QString &str, int pos)
+{
+ QChar::Joining join = nextChar( str, pos )->joining();
+ return ( join == QChar::Dual || join == QChar::Center );
+}
+
+
+KoComplexText::Shape KoComplexText::glyphVariant( const QString &str, int pos)
+{
+ // ignores L1 - L3, done in the codec
+ QChar::Joining joining = str[pos].joining();
+ //kdDebug() << "checking " << str[pos].unicode() << ", joining=" << joining << endl;
+ switch ( joining ) {
+ case QChar::OtherJoining:
+ case QChar::Center:
+ // these don't change shape
+ return XIsolated;
+ case QChar::Right:
+ // only rule R2 applies
+ if( nextVisualCharJoins( str, pos ) )
+ return XFinal;
+ return XIsolated;
+ case QChar::Dual:
+ bool right = nextVisualCharJoins( str, pos );
+ bool left = prevVisualCharJoins( str, pos );
+ //kdDebug() << "dual: right=" << right << ", left=" << left << endl;
+ if( right && left )
+ return XMedial;
+ else if ( right )
+ return XFinal;
+ else if ( left )
+ return XInitial;
+ else
+ return XIsolated;
+ }
+ return XIsolated;
+}
+
+/* and the same thing for logical ordering :)
+ */
+static inline bool prevLogicalCharJoins( const QString &str, int pos)
+{
+ return ( nextChar( str, pos )->joining() != QChar::OtherJoining );
+}
+
+static inline bool nextLogicalCharJoins( const QString &str, int pos)
+{
+ QChar::Joining join = prevChar( str, pos )->joining();
+ return ( join == QChar::Dual || join == QChar::Center );
+}
+
+
+KoComplexText::Shape KoComplexText::glyphVariantLogical( const QString &str, int pos)
+{
+ // ignores L1 - L3, ligatures are job of the codec
+ QChar::Joining joining = str[pos].joining();
+ //kdDebug() << "checking " << str[pos].unicode() << ", joining=" << joining << endl;
+ switch ( joining ) {
+ case QChar::OtherJoining:
+ case QChar::Center:
+ // these don't change shape
+ return XIsolated;
+ case QChar::Right:
+ // only rule R2 applies
+ if( nextLogicalCharJoins( str, pos ) )
+ return XFinal;
+ return XIsolated;
+ case QChar::Dual:
+ bool right = nextLogicalCharJoins( str, pos );
+ bool left = prevLogicalCharJoins( str, pos );
+ //kdDebug() << "dual: right=" << right << ", left=" << left << endl;
+ if( right && left )
+ return XMedial;
+ else if ( right )
+ return XFinal;
+ else if ( left )
+ return XInitial;
+ else
+ return XIsolated;
+ }
+ return XIsolated;
+}
+
+// -------------------------------------------------------------
+
+// The unicode to unicode shaping codec.
+// does only presentation forms B at the moment, but that should be enough for
+// simple display
+static const ushort arabicUnicodeMapping[256][2] = {
+ // base of shaped forms, and number-1 of them ( 0 for non shaping,
+ // 1 for right binding and 3 for dual binding
+ { 0x0600, 0 }, // 0x600
+ { 0x0601, 0 }, // 0x601
+ { 0x0602, 0 }, // 0x602
+ { 0x0603, 0 }, // 0x603
+ { 0x0604, 0 }, // 0x604
+ { 0x0605, 0 }, // 0x605
+ { 0x0606, 0 }, // 0x606
+ { 0x0607, 0 }, // 0x607
+ { 0x0608, 0 }, // 0x608
+ { 0x0609, 0 }, // 0x609
+ { 0x060a, 0 }, // 0x60a
+ { 0x060b, 0 }, // 0x60b
+ { 0x060c, 0 }, // 0x60c Arabic comma
+ { 0x060d, 0 }, // 0x60d
+ { 0x060e, 0 }, // 0x60e
+ { 0x060f, 0 }, // 0x60f
+
+ { 0x0610, 0 }, // 0x610
+ { 0x0611, 0 }, // 0x611
+ { 0x0612, 0 }, // 0x612
+ { 0x0613, 0 }, // 0x613
+ { 0x0614, 0 }, // 0x614
+ { 0x0615, 0 }, // 0x615
+ { 0x0616, 0 }, // 0x616
+ { 0x0617, 0 }, // 0x617
+ { 0x0618, 0 }, // 0x618
+ { 0x0619, 0 }, // 0x619
+ { 0x061a, 0 }, // 0x61a
+ { 0x061b, 0 }, // 0x61b Arabic semicolon
+ { 0x061c, 0 }, // 0x61c
+ { 0x061d, 0 }, // 0x61d
+ { 0x061e, 0 }, // 0x61e
+ { 0x061f, 0 }, // 0x61f Arabic question mark
+
+ { 0x0620, 0 }, // 0x620
+ { 0xfe80, 0 }, // 0x621 Hamza
+ { 0xfe81, 1 }, // 0x622 R Alef with Madda above
+ { 0xfe83, 1 }, // 0x623 R Alef with Hamza above
+ { 0xfe85, 1 }, // 0x624 R Waw with Hamza above
+ { 0xfe87, 1 }, // 0x625 R Alef with Hamza below
+ { 0xfe89, 3 }, // 0x626 D Yeh with Hamza above
+ { 0xfe8d, 1 }, // 0x627 R Alef
+ { 0xfe8f, 3 }, // 0x628 D Beh
+ { 0xfe93, 1 }, // 0x629 R Teh Marbuta
+ { 0xfe95, 3 }, // 0x62a D Teh
+ { 0xfe99, 3 }, // 0x62b D Theh
+ { 0xfe9d, 3 }, // 0x62c D Jeem
+ { 0xfea1, 3 }, // 0x62d D Hah
+ { 0xfea5, 3 }, // 0x62e D Khah
+ { 0xfea9, 1 }, // 0x62f R Dal
+
+ { 0xfeab, 1 }, // 0x630 R Thal
+ { 0xfead, 1 }, // 0x631 R Reh
+ { 0xfeaf, 1 }, // 0x632 R Zain
+ { 0xfeb1, 1 }, // 0x633 D Seen
+ { 0xfeb5, 3 }, // 0x634 D Sheen
+ { 0xfeb9, 3 }, // 0x635 D Sad
+ { 0xfebd, 3 }, // 0x636 D Dad
+ { 0xfec1, 3 }, // 0x637 D Tah
+ { 0xfec5, 3 }, // 0x638 D Zah
+ { 0xfec9, 3 }, // 0x639 D Ain
+ { 0xfecd, 3 }, // 0x63a D Ghain
+ { 0x063b, 0 }, // 0x63b
+ { 0x063c, 0 }, // 0x63c
+ { 0x063d, 0 }, // 0x63d
+ { 0x063e, 0 }, // 0x63e
+ { 0x063f, 0 }, // 0x63f
+
+ { 0x0640, 0 }, // 0x640 C Tatweel
+ { 0xfed1, 3 }, // 0x641 D Feh
+ { 0xfed5, 3 }, // 0x642 D Qaf
+ { 0xfed9, 3 }, // 0x643 D Kaf
+ { 0xfedd, 3 }, // 0x644 D Lam
+ { 0xfee1, 3 }, // 0x645 D Meem
+ { 0xfee5, 3 }, // 0x646 D Noon
+ { 0xfee9, 3 }, // 0x647 D Heh
+ { 0xfeed, 1 }, // 0x648 R Waw
+ { 0xfeef, 1 }, // 0x649 R Alef Maksura // ### Dual according to newest arabicshaping.txt
+ { 0xfef1, 3 }, // 0x64a D Yeh
+ { 0x064b, 0 }, // 0x64b Mark Fathatan
+ { 0x064c, 0 }, // 0x64c Mark Dammatan
+ { 0x064d, 0 }, // 0x64d Mark Kasratan
+ { 0x064e, 0 }, // 0x64e Mark Fatha
+ { 0x064f, 0 }, // 0x64f Mark Damma
+
+ { 0x0650, 0 }, // 0x650 Mark Kasra
+ { 0x0651, 0 }, // 0x651 Mark Shadda
+ { 0x0652, 0 }, // 0x652 Mark Sukan
+ // these do not exist in latin6 anymore:
+ { 0x0653, 0 }, // 0x653 Mark Maddah above
+ { 0x0654, 0 }, // 0x654 Mark Hamza above
+ { 0x0655, 0 }, // 0x655 Mark Hamza below
+ { 0x0656, 0 }, // 0x656
+ { 0x0657, 0 }, // 0x657
+ { 0x0658, 0 }, // 0x658
+ { 0x0659, 0 }, // 0x659
+ { 0x065a, 0 }, // 0x65a
+ { 0x065b, 0 }, // 0x65b
+ { 0x065c, 0 }, // 0x65c
+ { 0x065d, 0 }, // 0x65d
+ { 0x065e, 0 }, // 0x65e
+ { 0x065f, 0 }, // 0x65f
+
+ { 0x0660, 0 }, // 0x660 Arabic 0
+ { 0x0661, 0 }, // 0x661 Arabic 1
+ { 0x0662, 0 }, // 0x662 Arabic 2
+ { 0x0663, 0 }, // 0x663 Arabic 3
+ { 0x0664, 0 }, // 0x664 Arabic 4
+ { 0x0665, 0 }, // 0x665 Arabic 5
+ { 0x0666, 0 }, // 0x666 Arabic 6
+ { 0x0667, 0 }, // 0x667 Arabic 7
+ { 0x0668, 0 }, // 0x668 Arabic 8
+ { 0x0669, 0 }, // 0x669 Arabic 9
+ { 0x066a, 0 }, // 0x66a Arabic % sign
+ { 0x066b, 0 }, // 0x66b Arabic decimal separator
+ { 0x066c, 0 }, // 0x66c Arabic thousands separator
+ { 0x066d, 0 }, // 0x66d Arabic five pointed star
+ { 0x066e, 0 }, // 0x66e
+ { 0x066f, 0 }, // 0x66f
+
+ // ### some glyphs do not have shaped mappings in the presentation forms A.
+ // these have the shaping set to 0 for the moment. Will have to find out better mappings for them.
+ { 0x0670, 0 }, // 0x670
+ { 0xfb50, 1 }, // 0x671 R Alef Wasla
+ { 0x0672, 0 }, // 0x672 R Alef with wavy Hamza above
+ { 0x0673, 0 }, // 0x673 R Alef with wavy Hamza below
+ { 0x0674, 0 }, // 0x674 U High Hamza
+ { 0x0675, 0 }, // 0x675 R High Hamza Alef
+ { 0x0676, 0 }, // 0x676 R High Hamza Wav
+ { 0xfbdd, 0 }, // 0x677 R U with hamza above // ### only isolated form found...
+ { 0x0678, 0 }, // 0x678 D High hamza yeh
+ { 0xfb66, 3 }, // 0x679 D ttheh
+ { 0xfb5e, 3 }, // 0x67a D theheh
+ { 0xfb52, 3 }, // 0x67b D beeh
+ { 0x067c, 0 }, // 0x67cD teh with ring
+ { 0x067d, 0 }, // 0x67d D teh with three dots above downwards
+ { 0xfb56, 3 }, // 0x67e D peh
+ { 0xfb62, 3 }, // 0x67f D teheh
+
+ { 0xfb5a, 3 }, // 0x680 D beheh
+ { 0x0681, 0 }, // 0x681 D hah with hamza above
+ { 0x0682, 0 }, // 0x682 D hah with two dots vertical above
+ { 0xfb76, 3 }, // 0x683 D nyeh
+ { 0xfb72, 3 }, // 0x684 D dyeh
+ { 0x0685, 0 }, // 0x685 D hah with three dots above
+ { 0xfb7a, 3 }, // 0x686 D tcheh
+ { 0xfb7e, 3 }, // 0x687 D tcheheh
+ { 0xfb88, 1 }, // 0x688 R ddal
+ { 0x0689, 0 }, // 0x689 R dal with ring
+ { 0x068a, 0 }, // 0x68a R dal with dot
+ { 0x068b, 0 }, // 0x68b R dal with dot below and small tah
+ { 0xfb84, 1 }, // 0x68cR dahal
+ { 0xfb82, 1 }, // 0x68d R ddahal
+ { 0xfb86, 1 }, // 0x68e R dul
+ { 0x068f, 0 }, // 0x68f R dal with three dots above downwards
+
+ { 0x0690, 0 }, // 0x690 R dal with four dots above
+ { 0xfb8c, 1 }, // 0x691 R rreh
+ { 0x0692, 0 }, // 0x692 R reh with small v
+ { 0x0693, 0 }, // 0x693 R reh with ring
+ { 0x0694, 0 }, // 0x694 R reh with dot below
+ { 0x0695, 0 }, // 0x695 R reh with small v below
+ { 0x0696, 0 }, // 0x696 R reh with dot below and dot above
+ { 0x0697, 0 }, // 0x697 R reh with two dots above
+ { 0xfb8a, 1 }, // 0x698 R jeh
+ { 0x0699, 0 }, // 0x699 R reh with four dots above
+ { 0x069a, 0 }, // 0x69a D seen with dot below and dot above
+ { 0x069b, 0 }, // 0x69b D seen with three dots below
+ { 0x069c, 0 }, // 0x69cD seen with three dots below and three dots above
+ { 0x069d, 0 }, // 0x69d D sad with two dots below
+ { 0x069e, 0 }, // 0x69e D sad with three dots above
+ { 0x069f, 0 }, // 0x69f D tah with three dots above
+
+ { 0x06a0, 0 }, // 0x6a0 D ain with three dots above
+ { 0x06a1, 0 }, // 0x6a1 D dotless feh
+ { 0x06a2, 0 }, // 0x6a2 D feh with dot moved below
+ { 0x06a3, 0 }, // 0x6a3 D feh with dot below
+ { 0xfb6a, 3 }, // 0x6a4 D veh
+ { 0x06a5, 0 }, // 0x6a5 D feh with three dots below
+ { 0xfb6e, 3 }, // 0x6a6 D peheh
+ { 0x06a7, 0 }, // 0x6a7 D qaf with dot above
+ { 0x06a8, 0 }, // 0x6a8 D qaf woith three dots above
+ { 0xfb8e, 3 }, // 0x6a9 D keheh
+ { 0x06aa, 0 }, // 0x6aa D swash kaf
+ { 0x06ab, 0 }, // 0x6ab D kaf with ring
+ { 0x06ac, 0 }, // 0x6acD kaf with dot above
+ { 0xfbd3, 3 }, // 0x6ad D ng
+ { 0x06ae, 0 }, // 0x6ae D kaf with three dots below
+ { 0xfb92, 3 }, // 0x6af D gaf
+
+ { 0x06b0, 0 }, // 0x6b0 D gaf with ring
+ { 0xfb9a, 3 }, // 0x6b1 D ngoeh
+ { 0x06b2, 0 }, // 0x6b2 D gaf with two dots below
+ { 0xfb96, 3 }, // 0x6b3 D gueh
+ { 0x06b4, 0 }, // 0x6b4 D gaf with three dots above
+ { 0x06b5, 0 }, // 0x6b5 D lam with small v
+ { 0x06b6, 0 }, // 0x6b6 D lam with dot above
+ { 0x06b7, 0 }, // 0x6b7 D lam with three dots above
+ { 0x06b8, 0 }, // 0x6b8 D lam with three dots below
+ { 0x06b9, 0 }, // 0x6b9 D noon with dot below
+ { 0xfb9e, 1 }, // 0x6ba R noon ghunna
+ { 0xfba0, 3 }, // 0x6bb D rnoon
+ { 0x06bc, 0 }, // 0x6bcD noon with ring
+ { 0x06bd, 0 }, // 0x6bd D noon with three dots above
+ { 0xfbaa, 3 }, // 0x6be D heh doachashmee
+ { 0x06bf, 0 }, // 0x6bf D tcheh with dot above
+
+ { 0xfba4, 1 }, // 0x6c0 R heh with yeh above = ligature hamza on hah (06d5 + 0654)
+ { 0xfba6, 3 }, // 0x6c1 D heh goal
+ { 0x06c2, 0 }, // 0x6c2 R heh goal with hamza above (06c1 + 0654)
+ { 0x06c3, 0 }, // 0x6c3 R teh marbuta goal
+ { 0x06c4, 0 }, // 0x6c4 R waw with ring
+ { 0xfbe0, 1 }, // 0x6c5 R kirghiz oe
+ { 0xfbd9, 1 }, // 0x6c6 R oe
+ { 0xfbd7, 1 }, // 0x6c7 R u
+ { 0xfbdb, 1 }, // 0x6c8 R yu
+ { 0xfbe2, 1 }, // 0x6c9 R kirghiz yu
+ { 0x06ca, 0 }, // 0x6ca R waw with teo dots above
+ { 0xfbde, 1 }, // 0x6cb R ve
+ { 0x06cc, 0 }, // 0x6cc D farsi yeh
+ { 0x06cd, 0 }, // 0x6cd R yeh with tail
+ { 0x06ce, 0 }, // 0x6ce D yeh with small v
+ { 0x06cf, 0 }, // 0x6cf R waw with dot above
+
+ { 0xfbe4, 3 }, // 0x6d0 D e
+ { 0x06d1, 0 }, // 0x6d1 D yeh with three dots below
+ { 0xfbae, 1 }, // 0x6d2 R yeh barree
+ { 0xfbb0, 1 }, // 0x6d3 R yeh barree with hamza above
+ { 0x06d4, 0 }, // 0x6d4 U full stop
+ { 0x06d5, 0 }, // 0x6d5 D ae
+ { 0x06d6, 0 }, // 0x6d6 koreanic annotaion signs
+ { 0x06d7, 0 }, // 0x6d7 ...
+ { 0x06d8, 0 }, // 0x6d8
+ { 0x06d9, 0 }, // 0x6d9
+ { 0x06da, 0 }, // 0x6da
+ { 0x06db, 0 }, // 0x6db
+ { 0x06dc, 0 }, // 0x6dc
+ { 0x06dd, 0 }, // 0x6dd
+ { 0x06de, 0 }, // 0x6de
+ { 0x06df, 0 }, // 0x6df
+
+ { 0x06e0, 0 }, // 0x6e0
+ { 0x06e1, 0 }, // 0x6e1
+ { 0x06e2, 0 }, // 0x6e2
+ { 0x06e3, 0 }, // 0x6e3
+ { 0x06e4, 0 }, // 0x6e4
+ { 0x06e5, 0 }, // 0x6e5
+ { 0x06e6, 0 }, // 0x6e6
+ { 0x06e7, 0 }, // 0x6e7
+ { 0x06e8, 0 }, // 0x6e8
+ { 0x06e9, 0 }, // 0x6e9
+ { 0x06ea, 0 }, // 0x6ea
+ { 0x06eb, 0 }, // 0x6eb
+ { 0x06ec, 0 }, // 0x6ec
+ { 0x06ed, 0 }, // 0x6ed
+ { 0x06ee, 0 }, // 0x6ee
+ { 0x06ef, 0 }, // 0x6ef
+
+ { 0x06f0, 0 }, // 0x6f0 Arabic indic digit 0
+ { 0x06f1, 0 }, // 0x6f1
+ { 0x06f2, 0 }, // 0x6f2
+ { 0x06f3, 0 }, // 0x6f3
+ { 0x06f4, 0 }, // 0x6f4
+ { 0x06f5, 0 }, // 0x6f5
+ { 0x06f6, 0 }, // 0x6f6
+ { 0x06f7, 0 }, // 0x6f7
+ { 0x06f8, 0 }, // 0x6f8
+ { 0x06f9, 0 }, // 0x6f9 Arabic indic digit 9
+ { 0x06fa, 0 }, // 0x6fa D Sheen with dot below
+ { 0x06fb, 0 }, // 0x6fb D dad with dot below
+ { 0x06fc, 0 }, // 0x6fc D ghain with dot below
+ { 0x06fd, 0 }, // 0x6fd Sindhi ampersand
+ { 0x06fe, 0 }, // 0x6fe sindhi postposition
+ { 0x06ff, 0 }, // 0x6ff
+
+};
+
+// this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
+// of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
+// medial to the final form
+static const ushort arabicUnicodeLamAlefMapping[6][4] = {
+ { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, // 0x622 R Alef with Madda above
+ { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, // 0x623 R Alef with Hamza above
+ { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x624 R Waw with Hamza above
+ { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, // 0x625 R Alef with Hamza below
+ { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x626 D Yeh with Hamza above
+ { 0xfffd, 0xfffd, 0xfefb, 0xfefc } // 0x627 R Alef
+};
+
+static inline int getShape( const QChar * /* base */, uchar cell, int shape,
+ const QFontMetrics * /* fm */ )
+{
+ uint ch = arabicUnicodeMapping[cell][0] + shape;
+ /*
+ // we revert to the unshaped glyph in case the shaped version doesn't exist
+ if ( fm && !fm->inFont( ch ) ) {
+ switch( shape ) {
+ case KoComplexText::XIsolated:
+ break; // try base form
+ case KoComplexText::XFinal:
+ ch -= 1; // try isolated form
+ break;
+ case KoComplexText::XInitial:
+ ch += 1; // try medial form
+ break;
+ case KoComplexText::XMedial:
+ ch -= 1; // try initial form
+ break;
+ }
+ if ( !fm->inFont( ch ) )
+ ch = *base;
+ }
+ */
+ return ch;
+}
+
+QString KoComplexText::shapedString(const QString& uc, int from, int len, QPainter::TextDirection dir, const QFontMetrics *fm )
+{
+ if( len < 0 )
+ len = uc.length() - from;
+ if( len == 0 ) {
+ return QString::null;
+ }
+
+ // we have to ignore NSMs at the beginning and add at the end.
+ int num = uc.length() - from - len;
+ const QChar *ch = uc.unicode() + from + len;
+ while ( num > 0 && ch->combiningClass() != 0 ) {
+ ch++;
+ num--;
+ len++;
+ }
+ ch = uc.unicode() + from;
+ while ( len > 0 && ch->combiningClass() != 0 ) {
+ ch++;
+ len--;
+ from++;
+ }
+ if ( len == 0 ) return QString::null;
+
+ if( !shapeBuffer || len > shapeBufSize ) {
+ if( shapeBuffer ) free( (void *) shapeBuffer );
+ shapeBuffer = (QChar *) malloc( len*sizeof( QChar ) );
+// delete [] shapeBuffer;
+// shapeBuffer = new QChar[ len + 1];
+ shapeBufSize = len;
+ }
+
+ int lenOut = 0;
+ QChar *data = shapeBuffer;
+ if ( dir == QPainter::RTL )
+ ch += len - 1;
+ for ( int i = 0; i < len; i++ ) {
+ uchar r = ch->row();
+ uchar c = ch->cell();
+ if ( r != 0x06 ) {
+ if ( dir == QPainter::RTL && ch->mirrored() )
+ *data = ch->mirroredChar();
+ else
+ *data = *ch;
+ data++;
+ lenOut++;
+ } else {
+ int pos = i + from;
+ if ( dir == QPainter::RTL )
+ pos = from + len - 1 - i;
+ int shape = glyphVariantLogical( uc, pos );
+ //kdDebug() << "mapping U+" << ch->unicode() << " to shape " << shape << " glyph=0x" << arabicUnicodeMapping[ch->cell()][shape] << endl;
+ // take care of lam-alef ligatures (lam right of alef)
+ ushort map;
+ switch ( c ) {
+ case 0x44: { // lam
+ const QChar *pch = nextChar( uc, pos );
+ if ( pch->row() == 0x06 ) {
+ switch ( pch->cell() ) {
+ case 0x22:
+ case 0x23:
+ case 0x25:
+ case 0x27:
+ //kdDebug() << " lam of lam-alef ligature" << endl;
+ map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
+ goto next;
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case 0x22: // alef with madda
+ case 0x23: // alef with hamza above
+ case 0x25: // alef with hamza below
+ case 0x27: // alef
+ if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
+ // have a lam alef ligature
+ //kdDebug() << " alef of lam-alef ligature" << endl;
+ goto skip;
+ }
+ default:
+ break;
+ }
+ map = getShape( ch, c, shape, fm );
+ next:
+ *data = map;
+ data++;
+ lenOut++;
+ }
+ skip:
+ if ( dir == QPainter::RTL )
+ ch--;
+ else
+ ch++;
+ }
+
+ if ( dir == QPainter::Auto && !uc.simpleText() ) {
+ return bidiReorderString( QConstString( shapeBuffer, lenOut ).string() );
+ }
+ if ( dir == QPainter::RTL ) {
+ // reverses the non spacing marks to be again after the base char
+ QChar *s = shapeBuffer;
+ int i = 0;
+ while ( i < lenOut ) {
+ if ( s->combiningClass() != 0 ) {
+ // non spacing marks
+ int clen = 1;
+ QChar *ch = s;
+ do {
+ ch++;
+ clen++;
+ } while ( ch->combiningClass() != 0 );
+
+ int j = 0;
+ QChar *cp = s;
+ while ( j < clen/2 ) {
+ QChar tmp = *cp;
+ *cp = *ch;
+ *ch = tmp;
+ cp++;
+ ch--;
+ j++;
+ }
+ s += clen;
+ i += clen;
+ } else {
+ s++;
+ i++;
+ }
+ }
+ }
+
+ return QConstString( shapeBuffer, lenOut ).string();
+}
+
+QChar KoComplexText::shapedCharacter( const QString &str, int pos, const QFontMetrics *fm )
+{
+ const QChar *ch = str.unicode() + pos;
+ if ( ch->row() != 0x06 )
+ return *ch;
+ else {
+ int shape = glyphVariantLogical( str, pos );
+ //kdDebug() << "mapping U+" << ch->unicode() << " to shape " << shape << " glyph=0x" << arabicUnicodeMapping[ch->cell()][shape] << endl;
+ // lam aleph ligatures
+ switch ( ch->cell() ) {
+ case 0x44: { // lam
+ const QChar *nch = nextChar( str, pos );
+ if ( nch->row() == 0x06 ) {
+ switch ( nch->cell() ) {
+ case 0x22:
+ case 0x23:
+ case 0x25:
+ case 0x27:
+ return QChar(arabicUnicodeLamAlefMapping[nch->cell() - 0x22][shape]);
+ default:
+ break;
+ }
+ }
+ break;
+ }
+ case 0x22: // alef with madda
+ case 0x23: // alef with hamza above
+ case 0x25: // alef with hamza below
+ case 0x27: // alef
+ if ( prevChar( str, pos )->unicode() == 0x0644 )
+ // have a lam alef ligature
+ return QChar(0);
+ default:
+ break;
+ }
+ return QChar( getShape( ch, ch->cell(), shape, fm ) );
+ }
+}
+
+// Avoid using QFontPrivate, to which we don't have access. We don't use positionMarks() anyway
+#if 0
+QPointArray KoComplexText::positionMarks( QFontPrivate *f, const QString &str,
+ int pos, QRect *boundingRect )
+{
+ int len = str.length();
+ int nmarks = 0;
+ while ( pos + nmarks < len && str[pos+nmarks +1].combiningClass() > 0 )
+ nmarks++;
+
+ if ( !nmarks )
+ return QPointArray();
+
+ QChar baseChar = KoComplexText::shapedCharacter( str, pos );
+ QRect baseRect = f->boundingRect( baseChar );
+ int baseOffset = f->textWidth( str, pos, 1 );
+
+ //kdDebug() << "base char: bounding rect at " << baseRect.x() << "/" << baseRect.y() << " (" << baseRect.width() << "/" << baseRect.height() << ")" << endl;
+ int offset = f->actual.pixelSize / 10 + 1;
+ //kdDebug() << "offset = " << offset << endl;
+ QPointArray pa( nmarks );
+ int i;
+ unsigned char lastCmb = 0;
+ QRect attachmentRect;
+ if ( boundingRect )
+ *boundingRect = baseRect;
+ for( i = 0; i < nmarks; i++ ) {
+ QChar mark = str[pos+i+1];
+ unsigned char cmb = mark.combiningClass();
+ if ( cmb < 200 ) {
+ // fixed position classes. We approximate by mapping to one of the others.
+ // currently I added only the ones for arabic, hebrew and thai.
+
+ // ### add a bit more offset to arabic, a bit hacky
+ if ( cmb >= 27 && cmb <= 36 )
+ offset +=1;
+ // below
+ if ( (cmb >= 10 && cmb <= 18) ||
+ cmb == 20 || cmb == 22 ||
+ cmb == 29 || cmb == 32 )
+ cmb = QChar::Combining_Below;
+ // above
+ else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
+ cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
+ cmb = QChar::Combining_Above;
+ //below-right
+ else if ( cmb == 103 )
+ cmb = QChar::Combining_BelowRight;
+ // above-right
+ else if ( cmb == 24 || cmb == 107 )
+ cmb = QChar::Combining_AboveRight;
+ else if ( cmb == 25 )
+ cmb = QChar::Combining_AboveLeft;
+ // fixed:
+ // 19 21
+
+ }
+
+ // combining marks of different class don't interact. Reset the rectangle.
+ if ( cmb != lastCmb ) {
+ //kdDebug() << "resetting rect" << endl;
+ attachmentRect = baseRect;
+ }
+
+ QPoint p;
+ QRect markRect = f->boundingRect( mark );
+ switch( cmb ) {
+ case QChar::Combining_DoubleBelow:
+ // ### wrong in rtl context!
+ case QChar::Combining_BelowLeft:
+ p += QPoint( 0, offset );
+ case QChar::Combining_BelowLeftAttached:
+ p += attachmentRect.bottomLeft() - markRect.topLeft();
+ break;
+ case QChar::Combining_Below:
+ p += QPoint( 0, offset );
+ case QChar::Combining_BelowAttached:
+ p += attachmentRect.bottomLeft() - markRect.topLeft();
+ p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
+ break;
+ case QChar::Combining_BelowRight:
+ p += QPoint( 0, offset );
+ case QChar::Combining_BelowRightAttached:
+ p += attachmentRect.bottomRight() - markRect.topRight();
+ break;
+ case QChar::Combining_Left:
+ p += QPoint( -offset, 0 );
+ case QChar::Combining_LeftAttached:
+ break;
+ case QChar::Combining_Right:
+ p += QPoint( offset, 0 );
+ case QChar::Combining_RightAttached:
+ break;
+ case QChar::Combining_DoubleAbove:
+ // ### wrong in RTL context!
+ case QChar::Combining_AboveLeft:
+ p += QPoint( 0, -offset );
+ case QChar::Combining_AboveLeftAttached:
+ p += attachmentRect.topLeft() - markRect.bottomLeft();
+ break;
+ case QChar::Combining_Above:
+ p += QPoint( 0, -offset );
+ case QChar::Combining_AboveAttached:
+ p += attachmentRect.topLeft() - markRect.bottomLeft();
+ p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
+ break;
+ case QChar::Combining_AboveRight:
+ p += QPoint( 0, -offset );
+ case QChar::Combining_AboveRightAttached:
+ p += attachmentRect.topRight() - markRect.bottomRight();
+ break;
+
+ case QChar::Combining_IotaSubscript:
+ default:
+ break;
+ }
+ //kdDebug() << "char=" << mark.unicode() << " combiningClass = " << cmb << " offset=" << p.x() << "/" << p.y() << endl;
+ markRect.moveBy( p.x(), p.y() );
+ p += QPoint( -baseOffset, 0 );
+ attachmentRect |= markRect;
+ if ( boundingRect )
+ *boundingRect |= markRect;
+ lastCmb = cmb;
+ pa.setPoint( i, p );
+ }
+ return pa;
+}
+#endif
+
+//#define BIDI_DEBUG
+#ifdef BIDI_DEBUG
+#include <iostream>
+#endif
+
+static QChar::Direction basicDirection(const QString &str, int start = 0)
+{
+ int len = str.length();
+ int pos = start > len ? len -1 : start;
+ const QChar *uc = str.unicode() + pos;
+ while( pos < len ) {
+ switch( uc->direction() )
+ {
+ case QChar::DirL:
+ case QChar::DirLRO:
+ case QChar::DirLRE:
+ return QChar::DirL;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirRLO:
+ case QChar::DirRLE:
+ return QChar::DirR;
+ default:
+ break;
+ }
+ ++pos;
+ ++uc;
+ }
+ if ( start != 0 )
+ return basicDirection( str );
+ return QChar::DirL;
+}
+
+// transforms one line of the paragraph to visual order
+// the caller is responisble to delete the returned list of KoTextRuns.
+QPtrList<KoTextRun> *KoComplexText::bidiReorderLine( KoBidiControl *control, const QString &text, int start, int len,
+ QChar::Direction basicDir )
+{
+ int last = start + len - 1;
+ //printf("doing BiDi reordering from %d to %d!\n", start, last);
+
+ QPtrList<KoTextRun> *runs = new QPtrList<KoTextRun>;
+ runs->setAutoDelete(TRUE);
+
+ KoBidiContext *context = control->context;
+ if ( !context ) {
+ // first line
+ //if( start != 0 )
+ // kdDebug() << "bidiReorderLine::internal error" << endl;
+ if( basicDir == QChar::DirR || (basicDir == QChar::DirON && text.isRightToLeft() ) ) {
+ context = new KoBidiContext( 1, QChar::DirR );
+ control->status.last = QChar::DirR;
+ } else {
+ context = new KoBidiContext( 0, QChar::DirL );
+ control->status.last = QChar::DirL;
+ }
+ }
+
+ KoBidiStatus status = control->status;
+ QChar::Direction dir = QChar::DirON;
+
+ int sor = start;
+ int eor = start;
+
+ int current = start;
+ while(current <= last) {
+ QChar::Direction dirCurrent;
+ if(current == (int)text.length()) {
+ KoBidiContext *c = context;
+ while ( c->parent )
+ c = c->parent;
+ dirCurrent = c->dir;
+ } else if ( current == last ) {
+ dirCurrent = ( basicDir != QChar::DirON ? basicDir : basicDirection( text, current ) );
+ } else
+ dirCurrent = text.at(current).direction();
+
+
+#ifdef BIDI_DEBUG
+ cout << "directions: dir=" << dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << " level =" << (int)context->level << endl;
+#endif
+
+ switch(dirCurrent) {
+
+ // embedding and overrides (X1-X9 in the BiDi specs)
+ case QChar::DirRLE:
+ {
+ uchar level = context->level;
+ if(level%2) // we have an odd level
+ level += 2;
+ else
+ level++;
+ if(level < 61) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ context = new KoBidiContext(level, QChar::DirR, context);
+ status.last = QChar::DirR;
+ status.lastStrong = QChar::DirR;
+ }
+ break;
+ }
+ case QChar::DirLRE:
+ {
+ uchar level = context->level;
+ if(level%2) // we have an odd level
+ level++;
+ else
+ level += 2;
+ if(level < 61) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ context = new KoBidiContext(level, QChar::DirL, context);
+ status.last = QChar::DirL;
+ status.lastStrong = QChar::DirL;
+ }
+ break;
+ }
+ case QChar::DirRLO:
+ {
+ uchar level = context->level;
+ if(level%2) // we have an odd level
+ level += 2;
+ else
+ level++;
+ if(level < 61) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ context = new KoBidiContext(level, QChar::DirR, context, TRUE);
+ dir = QChar::DirR;
+ status.last = QChar::DirR;
+ status.lastStrong = QChar::DirR;
+ }
+ break;
+ }
+ case QChar::DirLRO:
+ {
+ uchar level = context->level;
+ if(level%2) // we have an odd level
+ level++;
+ else
+ level += 2;
+ if(level < 61) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ context = new KoBidiContext(level, QChar::DirL, context, TRUE);
+ dir = QChar::DirL;
+ status.last = QChar::DirL;
+ status.lastStrong = QChar::DirL;
+ }
+ break;
+ }
+ case QChar::DirPDF:
+ {
+ KoBidiContext *c = context->parent;
+ if(c) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ status.last = context->dir;
+ if( context->deref() ) delete context;
+ context = c;
+ if(context->override)
+ dir = context->dir;
+ else
+ dir = QChar::DirON;
+ status.lastStrong = context->dir;
+ }
+ break;
+ }
+
+ // strong types
+ case QChar::DirL:
+ if(dir == QChar::DirON)
+ dir = QChar::DirL;
+ switch(status.last)
+ {
+ case QChar::DirL:
+ eor = current; status.eor = QChar::DirL; break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ break;
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirCS:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(dir != QChar::DirL) {
+ //last stuff takes embedding dir
+ if( context->dir == QChar::DirR ) {
+ if(status.eor != QChar::DirR) {
+ // AN or EN
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirR;
+ }
+ else
+ eor = current - 1;
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ } else {
+ if(status.eor != QChar::DirL) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirL;
+ } else {
+ eor = current; status.eor = QChar::DirL; break;
+ }
+ }
+ } else {
+ eor = current; status.eor = QChar::DirL;
+ }
+ default:
+ break;
+ }
+ status.lastStrong = QChar::DirL;
+ break;
+ case QChar::DirAL:
+ case QChar::DirR:
+ if(dir == QChar::DirON) dir = QChar::DirR;
+ switch(status.last)
+ {
+ case QChar::DirR:
+ case QChar::DirAL:
+ eor = current; status.eor = QChar::DirR; break;
+ case QChar::DirL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ break;
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirCS:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if( status.eor != QChar::DirR && status.eor != QChar::DirAL ) {
+ //last stuff takes embedding dir
+ if(context->dir == QChar::DirR || status.lastStrong == QChar::DirR) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirR;
+ eor = current;
+ } else {
+ eor = current - 1;
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirR;
+ }
+ } else {
+ eor = current; status.eor = QChar::DirR;
+ }
+ default:
+ break;
+ }
+ status.lastStrong = dirCurrent;
+ break;
+
+ // weak types:
+
+ case QChar::DirNSM:
+ // ### if @sor, set dir to dirSor
+ break;
+ case QChar::DirEN:
+ if(status.lastStrong != QChar::DirAL) {
+ // if last strong was AL change EN to AL
+ if(dir == QChar::DirON) {
+ if(status.lastStrong == QChar::DirL)
+ dir = QChar::DirL;
+ else
+ dir = QChar::DirAN;
+ }
+ switch(status.last)
+ {
+ case QChar::DirET:
+ if ( status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL ) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; status.eor = QChar::DirON;
+ dir = QChar::DirAN;
+ }
+ // fall through
+ case QChar::DirEN:
+ case QChar::DirL:
+ eor = current;
+ status.eor = dirCurrent;
+ break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirAN:
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; status.eor = QChar::DirEN;
+ dir = QChar::DirAN; break;
+ case QChar::DirES:
+ case QChar::DirCS:
+ if(status.eor == QChar::DirEN || dir == QChar::DirAN) {
+ eor = current; break;
+ }
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(status.eor == QChar::DirR) {
+ // neutrals go to R
+ eor = current - 1;
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirEN;
+ dir = QChar::DirAN;
+ }
+ else if( status.eor == QChar::DirL ||
+ (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
+ eor = current; status.eor = dirCurrent;
+ } else {
+ // numbers on both sides, neutrals get right to left direction
+ if(dir != QChar::DirL) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ eor = current - 1;
+ dir = QChar::DirR;
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirAN;
+ } else {
+ eor = current; status.eor = dirCurrent;
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ }
+ case QChar::DirAN:
+ dirCurrent = QChar::DirAN;
+ if(dir == QChar::DirON) dir = QChar::DirAN;
+ switch(status.last)
+ {
+ case QChar::DirL:
+ case QChar::DirAN:
+ eor = current; status.eor = QChar::DirAN; break;
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ break;
+ case QChar::DirCS:
+ if(status.eor == QChar::DirAN) {
+ eor = current; status.eor = QChar::DirR; break;
+ }
+ case QChar::DirES:
+ case QChar::DirET:
+ case QChar::DirBN:
+ case QChar::DirB:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ if(status.eor == QChar::DirR) {
+ // neutrals go to R
+ eor = current - 1;
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirAN;
+ } else if( status.eor == QChar::DirL ||
+ (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
+ eor = current; status.eor = dirCurrent;
+ } else {
+ // numbers on both sides, neutrals get right to left direction
+ if(dir != QChar::DirL) {
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ eor = current - 1;
+ dir = QChar::DirR;
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+ ++eor; sor = eor; dir = QChar::DirON; status.eor = QChar::DirON;
+ dir = QChar::DirAN;
+ } else {
+ eor = current; status.eor = dirCurrent;
+ }
+ }
+ default:
+ break;
+ }
+ break;
+ case QChar::DirES:
+ case QChar::DirCS:
+ break;
+ case QChar::DirET:
+ if(status.last == QChar::DirEN) {
+ dirCurrent = QChar::DirEN;
+ eor = current; status.eor = dirCurrent;
+ break;
+ }
+ break;
+
+ // boundary neutrals should be ignored
+ case QChar::DirBN:
+ break;
+ // neutrals
+ case QChar::DirB:
+ // ### what do we do with newline and paragraph separators that come to here?
+ break;
+ case QChar::DirS:
+ // ### implement rule L1
+ break;
+ case QChar::DirWS:
+ case QChar::DirON:
+ break;
+ default:
+ break;
+ }
+
+ //cout << " after: dir=" << // dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << endl;
+
+ if(current >= (int)text.length()) break;
+
+ // set status.last as needed.
+ switch(dirCurrent)
+ {
+ case QChar::DirET:
+ case QChar::DirES:
+ case QChar::DirCS:
+ case QChar::DirS:
+ case QChar::DirWS:
+ case QChar::DirON:
+ switch(status.last)
+ {
+ case QChar::DirL:
+ case QChar::DirR:
+ case QChar::DirAL:
+ case QChar::DirEN:
+ case QChar::DirAN:
+ status.last = dirCurrent;
+ break;
+ default:
+ status.last = QChar::DirON;
+ }
+ break;
+ case QChar::DirNSM:
+ case QChar::DirBN:
+ // ignore these
+ break;
+ default:
+ status.last = dirCurrent;
+ }
+
+ ++current;
+ }
+
+#ifdef BIDI_DEBUG
+ cout << "reached end of line current=" << current << ", eor=" << eor << endl;
+#endif
+ eor = current - 1; // remove dummy char
+
+ if ( sor <= eor )
+ runs->append( new KoTextRun(sor, eor, context, dir) );
+
+ // reorder line according to run structure...
+
+ // first find highest and lowest levels
+ uchar levelLow = 128;
+ uchar levelHigh = 0;
+ KoTextRun *r = runs->first();
+ while ( r ) {
+ //printf("level = %d\n", r->level);
+ if ( r->level > levelHigh )
+ levelHigh = r->level;
+ if ( r->level < levelLow )
+ levelLow = r->level;
+ r = runs->next();
+ }
+
+ // implements reordering of the line (L2 according to BiDi spec):
+ // L2. From the highest level found in the text to the lowest odd level on each line,
+ // reverse any contiguous sequence of characters that are at that level or higher.
+
+ // reversing is only done up to the lowest odd level
+ if(!(levelLow%2)) levelLow++;
+
+#ifdef BIDI_DEBUG
+ cout << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
+ cout << "logical order is:" << endl;
+ QPtrListIterator<KoTextRun> it2(*runs);
+ KoTextRun *r2;
+ for ( ; (r2 = it2.current()); ++it2 )
+ cout << " " << r2 << " start=" << r2->start << " stop=" << r2->stop << " level=" << (uint)r2->level << endl;
+#endif
+
+ int count = runs->count() - 1;
+
+ while(levelHigh >= levelLow)
+ {
+ int i = 0;
+ while ( i < count )
+ {
+ while(i < count && runs->at(i)->level < levelHigh) i++;
+ int start = i;
+ while(i <= count && runs->at(i)->level >= levelHigh) i++;
+ int end = i-1;
+
+ if(start != end)
+ {
+ //cout << "reversing from " << start << " to " << end << endl;
+ for(int j = 0; j < (end-start+1)/2; j++)
+ {
+ KoTextRun *first = runs->take(start+j);
+ KoTextRun *last = runs->take(end-j-1);
+ runs->insert(start+j, last);
+ runs->insert(end-j, first);
+ }
+ }
+ i++;
+ if(i >= count) break;
+ }
+ levelHigh--;
+ }
+
+#ifdef BIDI_DEBUG
+ cout << "visual order is:" << endl;
+ QPtrListIterator<KoTextRun> it3(*runs);
+ KoTextRun *r3;
+ for ( ; (r3 = it3.current()); ++it3 )
+ {
+ cout << " " << r3 << endl;
+ }
+#endif
+
+ control->setContext( context );
+ control->status = status;
+
+ return runs;
+}
+
+
+QString KoComplexText::bidiReorderString( const QString &str, QChar::Direction /*basicDir*/ )
+{
+
+// ### fix basic direction
+ KoBidiControl control;
+ int lineStart = 0;
+ int lineEnd = 0;
+ int len = str.length();
+ QString visual;
+ visual.setUnicode( 0, len );
+ QChar *vch = (QChar *)visual.unicode();
+ const QChar *ch = str.unicode();
+ while( lineStart < len ) {
+ lineEnd = lineStart;
+ while( *ch != '\n' && lineEnd < len ) {
+ ch++;
+ lineEnd++;
+ }
+ lineEnd++;
+ QPtrList<KoTextRun> *runs = bidiReorderLine( &control, str, lineStart, lineEnd - lineStart );
+
+ // reorder the content of the line, and output to visual
+ KoTextRun *r = runs->first();
+ while ( r ) {
+ if(r->level %2) {
+ // odd level, need to reverse the string
+ int pos = r->stop;
+ while(pos >= r->start) {
+ *vch = str[pos];
+ if ( vch->mirrored() )
+ *vch = vch->mirroredChar();
+ vch++;
+ pos--;
+ }
+ } else {
+ int pos = r->start;
+ while(pos <= r->stop) {
+ *vch = str[pos];
+ vch++;
+ pos++;
+ }
+ }
+ r = runs->next();
+ }
+ if ( *ch == '\n' ) {
+ *vch = *ch;
+ vch++;
+ ch++;
+ lineEnd++;
+ }
+ lineStart = lineEnd;
+ }
+ return visual;
+}
+
+KoTextRun::KoTextRun(int _start, int _stop, KoBidiContext *context, QChar::Direction dir) {
+ start = _start;
+ stop = _stop;
+ if(dir == QChar::DirON) dir = context->dir;
+
+ level = context->level;
+
+ // add level of run (cases I1 & I2)
+ if( level % 2 ) {
+ if(dir == QChar::DirL || dir == QChar::DirAN)
+ level++;
+ } else {
+ if( dir == QChar::DirR )
+ level++;
+ else if( dir == QChar::DirAN )
+ level += 2;
+ }
+#ifdef BIDI_DEBUG
+ printf("new run: dir=%d from %d, to %d level = %d\n", dir, _start, _stop, level);
+#endif
+}
+
+#endif //QT_NO_COMPLEXTEXT