lib

KoComplexText.cpp
1/****************************************************************************
2** $Id: KoComplexText.cpp 504496 2006-02-01 11:16:49Z dfaure $
3**
4** Implementation of some internal classes
5**
6** Created :
7**
8** Copyright (C) 2001 Trolltech AS. All rights reserved.
9**
10** This file is part of the kernel module of the TQt GUI Toolkit.
11**
12** This file may be distributed under the terms of the Q Public License
13** as defined by Trolltech AS of Norway and appearing in the file
14** LICENSE.TQPL included in the packaging of this file.
15**
16** This file may be distributed and/or modified under the terms of the
17** GNU General Public License version 2 as published by the Free Software
18** Foundation and appearing in the file LICENSE.GPL included in the
19** packaging of this file.
20**
21** Licensees holding valid TQt Enterprise Edition or TQt Professional Edition
22** licenses may use this file in accordance with the TQt Commercial License
23** Agreement provided with the Software.
24**
25** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
26** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27**
28** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
29** information about TQt Commercial License Agreements.
30** See http://www.trolltech.com/qpl/ for TQPL licensing information.
31** See http://www.trolltech.com/gpl/ for GPL licensing information.
32**
33** Contact info@trolltech.com if any conditions of this licensing are
34** not clear to you.
35**
36**********************************************************************/
37
38
39#ifndef TQT_NO_COMPLEXTEXT
40#include "KoRichText.h"
41//#include "tqfontdata_p.h"
42#include "tqfontmetrics.h"
43#include "tqrect.h"
44
45#include <stdlib.h>
46
47// -----------------------------------------------------
48
49/* a small helper class used internally to resolve Bidi embedding levels.
50 Each line of text caches the embedding level at the start of the line for faster
51 relayouting
52*/
53KoBidiContext::KoBidiContext( uchar l, TQChar::Direction e, KoBidiContext *p, bool o )
54 : level(l) , override(o), dir(e)
55{
56 if ( p )
57 p->ref();
58 parent = p;
59 count = 0;
60}
61
62KoBidiContext::~KoBidiContext()
63{
64 if( parent && parent->deref() )
65 delete parent;
66}
67
68static TQChar *shapeBuffer = 0;
69static int shapeBufSize = 0;
70
71/*
72 Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
73 arabic).
74
75 Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
76 transparent joining is not encoded in TQChar::joining(), but applies to all combining marks and format marks.
77
78 Right join-causing: dual + center
79 Left join-causing: dual + right + center
80
81 Rules are as follows (for a string already in visual order, as we have it here):
82
83 R1 Transparent characters do not affect joining behaviour.
84 R2 A right joining character, that has a right join-causing char on the right will get form XRight
85 (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
86 Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
87 R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
88 the right will get form XMedial
89 R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
90 will get form XRight
91 R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
92 will get form XLeft
93 R7 Otherwise the character will get form XIsolated
94
95 Additionally we have to do the minimal ligature support for lam-alef ligatures:
96
97 L1 Transparent characters do not affect ligature behaviour.
98 L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
99 L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
100
101 The two functions defined in this class do shaping in visual and logical order. For logical order just replace right with
102 previous and left with next in the above rules ;-)
103*/
104
105/*
106 Two small helper functions for arabic shaping. They get the next shape causing character on either
107 side of the char in question. Implements rule R1.
108
109 leftChar() returns true if the char to the left is a left join-causing char
110 rightChar() returns true if the char to the right is a right join-causing char
111*/
112static inline const TQChar *prevChar( const TQString &str, int pos )
113{
114 //kdDebug() << "leftChar: pos=" << pos << endl;
115 pos--;
116 const TQChar *ch = str.unicode() + pos;
117 while( pos > -1 ) {
118 if( !ch->isMark() )
119 return ch;
120 pos--;
121 ch--;
122 }
123 return &TQChar::replacement;
124}
125
126static inline const TQChar *nextChar( const TQString &str, int pos)
127{
128 pos++;
129 int len = str.length();
130 const TQChar *ch = str.unicode() + pos;
131 while( pos < len ) {
132 //kdDebug() << "rightChar: " << pos << " isLetter=" << ch.isLetter() << ", joining=" << ch.joining() << endl;
133 if( !ch->isMark() )
134 return ch;
135 // assume it's a transparent char, this might not be 100% correct
136 pos++;
137 ch++;
138 }
139 return &TQChar::replacement;
140}
141
142static inline bool prevVisualCharJoins( const TQString &str, int pos)
143{
144 return ( prevChar( str, pos )->joining() != TQChar::OtherJoining );
145}
146
147static inline bool nextVisualCharJoins( const TQString &str, int pos)
148{
149 TQChar::Joining join = nextChar( str, pos )->joining();
150 return ( join == TQChar::Dual || join == TQChar::Center );
151}
152
153
154KoComplexText::Shape KoComplexText::glyphVariant( const TQString &str, int pos)
155{
156 // ignores L1 - L3, done in the codec
157 TQChar::Joining joining = str[pos].joining();
158 //kdDebug() << "checking " << str[pos].unicode() << ", joining=" << joining << endl;
159 switch ( joining ) {
160 case TQChar::OtherJoining:
161 case TQChar::Center:
162 // these don't change shape
163 return XIsolated;
164 case TQChar::Right:
165 // only rule R2 applies
166 if( nextVisualCharJoins( str, pos ) )
167 return XFinal;
168 return XIsolated;
169 case TQChar::Dual:
170 bool right = nextVisualCharJoins( str, pos );
171 bool left = prevVisualCharJoins( str, pos );
172 //kdDebug() << "dual: right=" << right << ", left=" << left << endl;
173 if( right && left )
174 return XMedial;
175 else if ( right )
176 return XFinal;
177 else if ( left )
178 return XInitial;
179 else
180 return XIsolated;
181 }
182 return XIsolated;
183}
184
185/* and the same thing for logical ordering :)
186 */
187static inline bool prevLogicalCharJoins( const TQString &str, int pos)
188{
189 return ( nextChar( str, pos )->joining() != TQChar::OtherJoining );
190}
191
192static inline bool nextLogicalCharJoins( const TQString &str, int pos)
193{
194 TQChar::Joining join = prevChar( str, pos )->joining();
195 return ( join == TQChar::Dual || join == TQChar::Center );
196}
197
198
199KoComplexText::Shape KoComplexText::glyphVariantLogical( const TQString &str, int pos)
200{
201 // ignores L1 - L3, ligatures are job of the codec
202 TQChar::Joining joining = str[pos].joining();
203 //kdDebug() << "checking " << str[pos].unicode() << ", joining=" << joining << endl;
204 switch ( joining ) {
205 case TQChar::OtherJoining:
206 case TQChar::Center:
207 // these don't change shape
208 return XIsolated;
209 case TQChar::Right:
210 // only rule R2 applies
211 if( nextLogicalCharJoins( str, pos ) )
212 return XFinal;
213 return XIsolated;
214 case TQChar::Dual:
215 bool right = nextLogicalCharJoins( str, pos );
216 bool left = prevLogicalCharJoins( str, pos );
217 //kdDebug() << "dual: right=" << right << ", left=" << left << endl;
218 if( right && left )
219 return XMedial;
220 else if ( right )
221 return XFinal;
222 else if ( left )
223 return XInitial;
224 else
225 return XIsolated;
226 }
227 return XIsolated;
228}
229
230// -------------------------------------------------------------
231
232// The unicode to unicode shaping codec.
233// does only presentation forms B at the moment, but that should be enough for
234// simple display
235static const ushort arabicUnicodeMapping[256][2] = {
236 // base of shaped forms, and number-1 of them ( 0 for non shaping,
237 // 1 for right binding and 3 for dual binding
238 { 0x0600, 0 }, // 0x600
239 { 0x0601, 0 }, // 0x601
240 { 0x0602, 0 }, // 0x602
241 { 0x0603, 0 }, // 0x603
242 { 0x0604, 0 }, // 0x604
243 { 0x0605, 0 }, // 0x605
244 { 0x0606, 0 }, // 0x606
245 { 0x0607, 0 }, // 0x607
246 { 0x0608, 0 }, // 0x608
247 { 0x0609, 0 }, // 0x609
248 { 0x060a, 0 }, // 0x60a
249 { 0x060b, 0 }, // 0x60b
250 { 0x060c, 0 }, // 0x60c Arabic comma
251 { 0x060d, 0 }, // 0x60d
252 { 0x060e, 0 }, // 0x60e
253 { 0x060f, 0 }, // 0x60f
254
255 { 0x0610, 0 }, // 0x610
256 { 0x0611, 0 }, // 0x611
257 { 0x0612, 0 }, // 0x612
258 { 0x0613, 0 }, // 0x613
259 { 0x0614, 0 }, // 0x614
260 { 0x0615, 0 }, // 0x615
261 { 0x0616, 0 }, // 0x616
262 { 0x0617, 0 }, // 0x617
263 { 0x0618, 0 }, // 0x618
264 { 0x0619, 0 }, // 0x619
265 { 0x061a, 0 }, // 0x61a
266 { 0x061b, 0 }, // 0x61b Arabic semicolon
267 { 0x061c, 0 }, // 0x61c
268 { 0x061d, 0 }, // 0x61d
269 { 0x061e, 0 }, // 0x61e
270 { 0x061f, 0 }, // 0x61f Arabic question mark
271
272 { 0x0620, 0 }, // 0x620
273 { 0xfe80, 0 }, // 0x621 Hamza
274 { 0xfe81, 1 }, // 0x622 R Alef with Madda above
275 { 0xfe83, 1 }, // 0x623 R Alef with Hamza above
276 { 0xfe85, 1 }, // 0x624 R Waw with Hamza above
277 { 0xfe87, 1 }, // 0x625 R Alef with Hamza below
278 { 0xfe89, 3 }, // 0x626 D Yeh with Hamza above
279 { 0xfe8d, 1 }, // 0x627 R Alef
280 { 0xfe8f, 3 }, // 0x628 D Beh
281 { 0xfe93, 1 }, // 0x629 R Teh Marbuta
282 { 0xfe95, 3 }, // 0x62a D Teh
283 { 0xfe99, 3 }, // 0x62b D Theh
284 { 0xfe9d, 3 }, // 0x62c D Jeem
285 { 0xfea1, 3 }, // 0x62d D Hah
286 { 0xfea5, 3 }, // 0x62e D Khah
287 { 0xfea9, 1 }, // 0x62f R Dal
288
289 { 0xfeab, 1 }, // 0x630 R Thal
290 { 0xfead, 1 }, // 0x631 R Reh
291 { 0xfeaf, 1 }, // 0x632 R Zain
292 { 0xfeb1, 1 }, // 0x633 D Seen
293 { 0xfeb5, 3 }, // 0x634 D Sheen
294 { 0xfeb9, 3 }, // 0x635 D Sad
295 { 0xfebd, 3 }, // 0x636 D Dad
296 { 0xfec1, 3 }, // 0x637 D Tah
297 { 0xfec5, 3 }, // 0x638 D Zah
298 { 0xfec9, 3 }, // 0x639 D Ain
299 { 0xfecd, 3 }, // 0x63a D Ghain
300 { 0x063b, 0 }, // 0x63b
301 { 0x063c, 0 }, // 0x63c
302 { 0x063d, 0 }, // 0x63d
303 { 0x063e, 0 }, // 0x63e
304 { 0x063f, 0 }, // 0x63f
305
306 { 0x0640, 0 }, // 0x640 C Tatweel
307 { 0xfed1, 3 }, // 0x641 D Feh
308 { 0xfed5, 3 }, // 0x642 D Qaf
309 { 0xfed9, 3 }, // 0x643 D Kaf
310 { 0xfedd, 3 }, // 0x644 D Lam
311 { 0xfee1, 3 }, // 0x645 D Meem
312 { 0xfee5, 3 }, // 0x646 D Noon
313 { 0xfee9, 3 }, // 0x647 D Heh
314 { 0xfeed, 1 }, // 0x648 R Waw
315 { 0xfeef, 1 }, // 0x649 R Alef Maksura // ### Dual according to newest arabicshaping.txt
316 { 0xfef1, 3 }, // 0x64a D Yeh
317 { 0x064b, 0 }, // 0x64b Mark Fathatan
318 { 0x064c, 0 }, // 0x64c Mark Dammatan
319 { 0x064d, 0 }, // 0x64d Mark Kasratan
320 { 0x064e, 0 }, // 0x64e Mark Fatha
321 { 0x064f, 0 }, // 0x64f Mark Damma
322
323 { 0x0650, 0 }, // 0x650 Mark Kasra
324 { 0x0651, 0 }, // 0x651 Mark Shadda
325 { 0x0652, 0 }, // 0x652 Mark Sukan
326 // these do not exist in latin6 anymore:
327 { 0x0653, 0 }, // 0x653 Mark Maddah above
328 { 0x0654, 0 }, // 0x654 Mark Hamza above
329 { 0x0655, 0 }, // 0x655 Mark Hamza below
330 { 0x0656, 0 }, // 0x656
331 { 0x0657, 0 }, // 0x657
332 { 0x0658, 0 }, // 0x658
333 { 0x0659, 0 }, // 0x659
334 { 0x065a, 0 }, // 0x65a
335 { 0x065b, 0 }, // 0x65b
336 { 0x065c, 0 }, // 0x65c
337 { 0x065d, 0 }, // 0x65d
338 { 0x065e, 0 }, // 0x65e
339 { 0x065f, 0 }, // 0x65f
340
341 { 0x0660, 0 }, // 0x660 Arabic 0
342 { 0x0661, 0 }, // 0x661 Arabic 1
343 { 0x0662, 0 }, // 0x662 Arabic 2
344 { 0x0663, 0 }, // 0x663 Arabic 3
345 { 0x0664, 0 }, // 0x664 Arabic 4
346 { 0x0665, 0 }, // 0x665 Arabic 5
347 { 0x0666, 0 }, // 0x666 Arabic 6
348 { 0x0667, 0 }, // 0x667 Arabic 7
349 { 0x0668, 0 }, // 0x668 Arabic 8
350 { 0x0669, 0 }, // 0x669 Arabic 9
351 { 0x066a, 0 }, // 0x66a Arabic % sign
352 { 0x066b, 0 }, // 0x66b Arabic decimal separator
353 { 0x066c, 0 }, // 0x66c Arabic thousands separator
354 { 0x066d, 0 }, // 0x66d Arabic five pointed star
355 { 0x066e, 0 }, // 0x66e
356 { 0x066f, 0 }, // 0x66f
357
358 // ### some glyphs do not have shaped mappings in the presentation forms A.
359 // these have the shaping set to 0 for the moment. Will have to find out better mappings for them.
360 { 0x0670, 0 }, // 0x670
361 { 0xfb50, 1 }, // 0x671 R Alef Wasla
362 { 0x0672, 0 }, // 0x672 R Alef with wavy Hamza above
363 { 0x0673, 0 }, // 0x673 R Alef with wavy Hamza below
364 { 0x0674, 0 }, // 0x674 U High Hamza
365 { 0x0675, 0 }, // 0x675 R High Hamza Alef
366 { 0x0676, 0 }, // 0x676 R High Hamza Wav
367 { 0xfbdd, 0 }, // 0x677 R U with hamza above // ### only isolated form found...
368 { 0x0678, 0 }, // 0x678 D High hamza yeh
369 { 0xfb66, 3 }, // 0x679 D ttheh
370 { 0xfb5e, 3 }, // 0x67a D theheh
371 { 0xfb52, 3 }, // 0x67b D beeh
372 { 0x067c, 0 }, // 0x67cD teh with ring
373 { 0x067d, 0 }, // 0x67d D teh with three dots above downwards
374 { 0xfb56, 3 }, // 0x67e D peh
375 { 0xfb62, 3 }, // 0x67f D teheh
376
377 { 0xfb5a, 3 }, // 0x680 D beheh
378 { 0x0681, 0 }, // 0x681 D hah with hamza above
379 { 0x0682, 0 }, // 0x682 D hah with two dots vertical above
380 { 0xfb76, 3 }, // 0x683 D nyeh
381 { 0xfb72, 3 }, // 0x684 D dyeh
382 { 0x0685, 0 }, // 0x685 D hah with three dots above
383 { 0xfb7a, 3 }, // 0x686 D tcheh
384 { 0xfb7e, 3 }, // 0x687 D tcheheh
385 { 0xfb88, 1 }, // 0x688 R ddal
386 { 0x0689, 0 }, // 0x689 R dal with ring
387 { 0x068a, 0 }, // 0x68a R dal with dot
388 { 0x068b, 0 }, // 0x68b R dal with dot below and small tah
389 { 0xfb84, 1 }, // 0x68cR dahal
390 { 0xfb82, 1 }, // 0x68d R ddahal
391 { 0xfb86, 1 }, // 0x68e R dul
392 { 0x068f, 0 }, // 0x68f R dal with three dots above downwards
393
394 { 0x0690, 0 }, // 0x690 R dal with four dots above
395 { 0xfb8c, 1 }, // 0x691 R rreh
396 { 0x0692, 0 }, // 0x692 R reh with small v
397 { 0x0693, 0 }, // 0x693 R reh with ring
398 { 0x0694, 0 }, // 0x694 R reh with dot below
399 { 0x0695, 0 }, // 0x695 R reh with small v below
400 { 0x0696, 0 }, // 0x696 R reh with dot below and dot above
401 { 0x0697, 0 }, // 0x697 R reh with two dots above
402 { 0xfb8a, 1 }, // 0x698 R jeh
403 { 0x0699, 0 }, // 0x699 R reh with four dots above
404 { 0x069a, 0 }, // 0x69a D seen with dot below and dot above
405 { 0x069b, 0 }, // 0x69b D seen with three dots below
406 { 0x069c, 0 }, // 0x69cD seen with three dots below and three dots above
407 { 0x069d, 0 }, // 0x69d D sad with two dots below
408 { 0x069e, 0 }, // 0x69e D sad with three dots above
409 { 0x069f, 0 }, // 0x69f D tah with three dots above
410
411 { 0x06a0, 0 }, // 0x6a0 D ain with three dots above
412 { 0x06a1, 0 }, // 0x6a1 D dotless feh
413 { 0x06a2, 0 }, // 0x6a2 D feh with dot moved below
414 { 0x06a3, 0 }, // 0x6a3 D feh with dot below
415 { 0xfb6a, 3 }, // 0x6a4 D veh
416 { 0x06a5, 0 }, // 0x6a5 D feh with three dots below
417 { 0xfb6e, 3 }, // 0x6a6 D peheh
418 { 0x06a7, 0 }, // 0x6a7 D qaf with dot above
419 { 0x06a8, 0 }, // 0x6a8 D qaf woith three dots above
420 { 0xfb8e, 3 }, // 0x6a9 D keheh
421 { 0x06aa, 0 }, // 0x6aa D swash kaf
422 { 0x06ab, 0 }, // 0x6ab D kaf with ring
423 { 0x06ac, 0 }, // 0x6acD kaf with dot above
424 { 0xfbd3, 3 }, // 0x6ad D ng
425 { 0x06ae, 0 }, // 0x6ae D kaf with three dots below
426 { 0xfb92, 3 }, // 0x6af D gaf
427
428 { 0x06b0, 0 }, // 0x6b0 D gaf with ring
429 { 0xfb9a, 3 }, // 0x6b1 D ngoeh
430 { 0x06b2, 0 }, // 0x6b2 D gaf with two dots below
431 { 0xfb96, 3 }, // 0x6b3 D gueh
432 { 0x06b4, 0 }, // 0x6b4 D gaf with three dots above
433 { 0x06b5, 0 }, // 0x6b5 D lam with small v
434 { 0x06b6, 0 }, // 0x6b6 D lam with dot above
435 { 0x06b7, 0 }, // 0x6b7 D lam with three dots above
436 { 0x06b8, 0 }, // 0x6b8 D lam with three dots below
437 { 0x06b9, 0 }, // 0x6b9 D noon with dot below
438 { 0xfb9e, 1 }, // 0x6ba R noon ghunna
439 { 0xfba0, 3 }, // 0x6bb D rnoon
440 { 0x06bc, 0 }, // 0x6bcD noon with ring
441 { 0x06bd, 0 }, // 0x6bd D noon with three dots above
442 { 0xfbaa, 3 }, // 0x6be D heh doachashmee
443 { 0x06bf, 0 }, // 0x6bf D tcheh with dot above
444
445 { 0xfba4, 1 }, // 0x6c0 R heh with yeh above = ligature hamza on hah (06d5 + 0654)
446 { 0xfba6, 3 }, // 0x6c1 D heh goal
447 { 0x06c2, 0 }, // 0x6c2 R heh goal with hamza above (06c1 + 0654)
448 { 0x06c3, 0 }, // 0x6c3 R teh marbuta goal
449 { 0x06c4, 0 }, // 0x6c4 R waw with ring
450 { 0xfbe0, 1 }, // 0x6c5 R kirghiz oe
451 { 0xfbd9, 1 }, // 0x6c6 R oe
452 { 0xfbd7, 1 }, // 0x6c7 R u
453 { 0xfbdb, 1 }, // 0x6c8 R yu
454 { 0xfbe2, 1 }, // 0x6c9 R kirghiz yu
455 { 0x06ca, 0 }, // 0x6ca R waw with teo dots above
456 { 0xfbde, 1 }, // 0x6cb R ve
457 { 0x06cc, 0 }, // 0x6cc D farsi yeh
458 { 0x06cd, 0 }, // 0x6cd R yeh with tail
459 { 0x06ce, 0 }, // 0x6ce D yeh with small v
460 { 0x06cf, 0 }, // 0x6cf R waw with dot above
461
462 { 0xfbe4, 3 }, // 0x6d0 D e
463 { 0x06d1, 0 }, // 0x6d1 D yeh with three dots below
464 { 0xfbae, 1 }, // 0x6d2 R yeh barree
465 { 0xfbb0, 1 }, // 0x6d3 R yeh barree with hamza above
466 { 0x06d4, 0 }, // 0x6d4 U full stop
467 { 0x06d5, 0 }, // 0x6d5 D ae
468 { 0x06d6, 0 }, // 0x6d6 koreanic annotaion signs
469 { 0x06d7, 0 }, // 0x6d7 ...
470 { 0x06d8, 0 }, // 0x6d8
471 { 0x06d9, 0 }, // 0x6d9
472 { 0x06da, 0 }, // 0x6da
473 { 0x06db, 0 }, // 0x6db
474 { 0x06dc, 0 }, // 0x6dc
475 { 0x06dd, 0 }, // 0x6dd
476 { 0x06de, 0 }, // 0x6de
477 { 0x06df, 0 }, // 0x6df
478
479 { 0x06e0, 0 }, // 0x6e0
480 { 0x06e1, 0 }, // 0x6e1
481 { 0x06e2, 0 }, // 0x6e2
482 { 0x06e3, 0 }, // 0x6e3
483 { 0x06e4, 0 }, // 0x6e4
484 { 0x06e5, 0 }, // 0x6e5
485 { 0x06e6, 0 }, // 0x6e6
486 { 0x06e7, 0 }, // 0x6e7
487 { 0x06e8, 0 }, // 0x6e8
488 { 0x06e9, 0 }, // 0x6e9
489 { 0x06ea, 0 }, // 0x6ea
490 { 0x06eb, 0 }, // 0x6eb
491 { 0x06ec, 0 }, // 0x6ec
492 { 0x06ed, 0 }, // 0x6ed
493 { 0x06ee, 0 }, // 0x6ee
494 { 0x06ef, 0 }, // 0x6ef
495
496 { 0x06f0, 0 }, // 0x6f0 Arabic indic digit 0
497 { 0x06f1, 0 }, // 0x6f1
498 { 0x06f2, 0 }, // 0x6f2
499 { 0x06f3, 0 }, // 0x6f3
500 { 0x06f4, 0 }, // 0x6f4
501 { 0x06f5, 0 }, // 0x6f5
502 { 0x06f6, 0 }, // 0x6f6
503 { 0x06f7, 0 }, // 0x6f7
504 { 0x06f8, 0 }, // 0x6f8
505 { 0x06f9, 0 }, // 0x6f9 Arabic indic digit 9
506 { 0x06fa, 0 }, // 0x6fa D Sheen with dot below
507 { 0x06fb, 0 }, // 0x6fb D dad with dot below
508 { 0x06fc, 0 }, // 0x6fc D ghain with dot below
509 { 0x06fd, 0 }, // 0x6fd Sindhi ampersand
510 { 0x06fe, 0 }, // 0x6fe sindhi postposition
511 { 0x06ff, 0 }, // 0x6ff
512
513};
514
515// this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
516// of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
517// medial to the final form
518static const ushort arabicUnicodeLamAlefMapping[6][4] = {
519 { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, // 0x622 R Alef with Madda above
520 { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, // 0x623 R Alef with Hamza above
521 { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x624 R Waw with Hamza above
522 { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, // 0x625 R Alef with Hamza below
523 { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x626 D Yeh with Hamza above
524 { 0xfffd, 0xfffd, 0xfefb, 0xfefc } // 0x627 R Alef
525};
526
527static inline int getShape( const TQChar * /* base */, uchar cell, int shape,
528 const TQFontMetrics * /* fm */ )
529{
530 uint ch = arabicUnicodeMapping[cell][0] + shape;
531 /*
532 // we revert to the unshaped glyph in case the shaped version doesn't exist
533 if ( fm && !fm->inFont( ch ) ) {
534 switch( shape ) {
535 case KoComplexText::XIsolated:
536 break; // try base form
537 case KoComplexText::XFinal:
538 ch -= 1; // try isolated form
539 break;
540 case KoComplexText::XInitial:
541 ch += 1; // try medial form
542 break;
543 case KoComplexText::XMedial:
544 ch -= 1; // try initial form
545 break;
546 }
547 if ( !fm->inFont( ch ) )
548 ch = *base;
549 }
550 */
551 return ch;
552}
553
554TQString KoComplexText::shapedString(const TQString& uc, int from, int len, TQPainter::TextDirection dir, const TQFontMetrics *fm )
555{
556 if( len < 0 )
557 len = uc.length() - from;
558 if( len == 0 ) {
559 return TQString();
560 }
561
562 // we have to ignore NSMs at the beginning and add at the end.
563 int num = uc.length() - from - len;
564 const TQChar *ch = uc.unicode() + from + len;
565 while ( num > 0 && ch->combiningClass() != 0 ) {
566 ch++;
567 num--;
568 len++;
569 }
570 ch = uc.unicode() + from;
571 while ( len > 0 && ch->combiningClass() != 0 ) {
572 ch++;
573 len--;
574 from++;
575 }
576 if ( len == 0 ) return TQString();
577
578 if( !shapeBuffer || len > shapeBufSize ) {
579 if( shapeBuffer ) free( (void *) shapeBuffer );
580 shapeBuffer = (TQChar *) malloc( len*sizeof( TQChar ) );
581// delete [] shapeBuffer;
582// shapeBuffer = new TQChar[ len + 1];
583 shapeBufSize = len;
584 }
585
586 int lenOut = 0;
587 TQChar *data = shapeBuffer;
588 if ( dir == TQPainter::RTL )
589 ch += len - 1;
590 for ( int i = 0; i < len; i++ ) {
591 uchar r = ch->row();
592 uchar c = ch->cell();
593 if ( r != 0x06 ) {
594 if ( dir == TQPainter::RTL && ch->mirrored() )
595 *data = ch->mirroredChar();
596 else
597 *data = *ch;
598 data++;
599 lenOut++;
600 } else {
601 int pos = i + from;
602 if ( dir == TQPainter::RTL )
603 pos = from + len - 1 - i;
604 int shape = glyphVariantLogical( uc, pos );
605 //kdDebug() << "mapping U+" << ch->unicode() << " to shape " << shape << " glyph=0x" << arabicUnicodeMapping[ch->cell()][shape] << endl;
606 // take care of lam-alef ligatures (lam right of alef)
607 ushort map;
608 switch ( c ) {
609 case 0x44: { // lam
610 const TQChar *pch = nextChar( uc, pos );
611 if ( pch->row() == 0x06 ) {
612 switch ( pch->cell() ) {
613 case 0x22:
614 case 0x23:
615 case 0x25:
616 case 0x27:
617 //kdDebug() << " lam of lam-alef ligature" << endl;
618 map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
619 goto next;
620 default:
621 break;
622 }
623 }
624 break;
625 }
626 case 0x22: // alef with madda
627 case 0x23: // alef with hamza above
628 case 0x25: // alef with hamza below
629 case 0x27: // alef
630 if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
631 // have a lam alef ligature
632 //kdDebug() << " alef of lam-alef ligature" << endl;
633 goto skip;
634 }
635 default:
636 break;
637 }
638 map = getShape( ch, c, shape, fm );
639 next:
640 *data = map;
641 data++;
642 lenOut++;
643 }
644 skip:
645 if ( dir == TQPainter::RTL )
646 ch--;
647 else
648 ch++;
649 }
650
651 if ( dir == TQPainter::Auto && !uc.simpleText() ) {
652 return bidiReorderString( TQConstString( shapeBuffer, lenOut ).string() );
653 }
654 if ( dir == TQPainter::RTL ) {
655 // reverses the non spacing marks to be again after the base char
656 TQChar *s = shapeBuffer;
657 int i = 0;
658 while ( i < lenOut ) {
659 if ( s->combiningClass() != 0 ) {
660 // non spacing marks
661 int clen = 1;
662 TQChar *ch = s;
663 do {
664 ch++;
665 clen++;
666 } while ( ch->combiningClass() != 0 );
667
668 int j = 0;
669 TQChar *cp = s;
670 while ( j < clen/2 ) {
671 TQChar tmp = *cp;
672 *cp = *ch;
673 *ch = tmp;
674 cp++;
675 ch--;
676 j++;
677 }
678 s += clen;
679 i += clen;
680 } else {
681 s++;
682 i++;
683 }
684 }
685 }
686
687 return TQConstString( shapeBuffer, lenOut ).string();
688}
689
690TQChar KoComplexText::shapedCharacter( const TQString &str, int pos, const TQFontMetrics *fm )
691{
692 const TQChar *ch = str.unicode() + pos;
693 if ( ch->row() != 0x06 )
694 return *ch;
695 else {
696 int shape = glyphVariantLogical( str, pos );
697 //kdDebug() << "mapping U+" << ch->unicode() << " to shape " << shape << " glyph=0x" << arabicUnicodeMapping[ch->cell()][shape] << endl;
698 // lam aleph ligatures
699 switch ( ch->cell() ) {
700 case 0x44: { // lam
701 const TQChar *nch = nextChar( str, pos );
702 if ( nch->row() == 0x06 ) {
703 switch ( nch->cell() ) {
704 case 0x22:
705 case 0x23:
706 case 0x25:
707 case 0x27:
708 return TQChar(arabicUnicodeLamAlefMapping[nch->cell() - 0x22][shape]);
709 default:
710 break;
711 }
712 }
713 break;
714 }
715 case 0x22: // alef with madda
716 case 0x23: // alef with hamza above
717 case 0x25: // alef with hamza below
718 case 0x27: // alef
719 if ( prevChar( str, pos )->unicode() == 0x0644 )
720 // have a lam alef ligature
721 return TQChar(0);
722 default:
723 break;
724 }
725 return TQChar( getShape( ch, ch->cell(), shape, fm ) );
726 }
727}
728
729// Avoid using TQFontPrivate, to which we don't have access. We don't use positionMarks() anyway
730#if 0
731TQPointArray KoComplexText::positionMarks( TQFontPrivate *f, const TQString &str,
732 int pos, TQRect *boundingRect )
733{
734 int len = str.length();
735 int nmarks = 0;
736 while ( pos + nmarks < len && str[pos+nmarks +1].combiningClass() > 0 )
737 nmarks++;
738
739 if ( !nmarks )
740 return TQPointArray();
741
742 TQChar baseChar = KoComplexText::shapedCharacter( str, pos );
743 TQRect baseRect = f->boundingRect( baseChar );
744 int baseOffset = f->textWidth( str, pos, 1 );
745
746 //kdDebug() << "base char: bounding rect at " << baseRect.x() << "/" << baseRect.y() << " (" << baseRect.width() << "/" << baseRect.height() << ")" << endl;
747 int offset = f->actual.pixelSize / 10 + 1;
748 //kdDebug() << "offset = " << offset << endl;
749 TQPointArray pa( nmarks );
750 int i;
751 unsigned char lastCmb = 0;
752 TQRect attachmentRect;
753 if ( boundingRect )
754 *boundingRect = baseRect;
755 for( i = 0; i < nmarks; i++ ) {
756 TQChar mark = str[pos+i+1];
757 unsigned char cmb = mark.combiningClass();
758 if ( cmb < 200 ) {
759 // fixed position classes. We approximate by mapping to one of the others.
760 // currently I added only the ones for arabic, hebrew and thai.
761
762 // ### add a bit more offset to arabic, a bit hacky
763 if ( cmb >= 27 && cmb <= 36 )
764 offset +=1;
765 // below
766 if ( (cmb >= 10 && cmb <= 18) ||
767 cmb == 20 || cmb == 22 ||
768 cmb == 29 || cmb == 32 )
769 cmb = TQChar::Combining_Below;
770 // above
771 else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
772 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
773 cmb = TQChar::Combining_Above;
774 //below-right
775 else if ( cmb == 103 )
776 cmb = TQChar::Combining_BelowRight;
777 // above-right
778 else if ( cmb == 24 || cmb == 107 )
779 cmb = TQChar::Combining_AboveRight;
780 else if ( cmb == 25 )
781 cmb = TQChar::Combining_AboveLeft;
782 // fixed:
783 // 19 21
784
785 }
786
787 // combining marks of different class don't interact. Reset the rectangle.
788 if ( cmb != lastCmb ) {
789 //kdDebug() << "resetting rect" << endl;
790 attachmentRect = baseRect;
791 }
792
793 TQPoint p;
794 TQRect markRect = f->boundingRect( mark );
795 switch( cmb ) {
796 case TQChar::Combining_DoubleBelow:
797 // ### wrong in rtl context!
798 case TQChar::Combining_BelowLeft:
799 p += TQPoint( 0, offset );
800 case TQChar::Combining_BelowLeftAttached:
801 p += attachmentRect.bottomLeft() - markRect.topLeft();
802 break;
803 case TQChar::Combining_Below:
804 p += TQPoint( 0, offset );
805 case TQChar::Combining_BelowAttached:
806 p += attachmentRect.bottomLeft() - markRect.topLeft();
807 p += TQPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
808 break;
809 case TQChar::Combining_BelowRight:
810 p += TQPoint( 0, offset );
811 case TQChar::Combining_BelowRightAttached:
812 p += attachmentRect.bottomRight() - markRect.topRight();
813 break;
814 case TQChar::Combining_Left:
815 p += TQPoint( -offset, 0 );
816 case TQChar::Combining_LeftAttached:
817 break;
818 case TQChar::Combining_Right:
819 p += TQPoint( offset, 0 );
820 case TQChar::Combining_RightAttached:
821 break;
822 case TQChar::Combining_DoubleAbove:
823 // ### wrong in RTL context!
824 case TQChar::Combining_AboveLeft:
825 p += TQPoint( 0, -offset );
826 case TQChar::Combining_AboveLeftAttached:
827 p += attachmentRect.topLeft() - markRect.bottomLeft();
828 break;
829 case TQChar::Combining_Above:
830 p += TQPoint( 0, -offset );
831 case TQChar::Combining_AboveAttached:
832 p += attachmentRect.topLeft() - markRect.bottomLeft();
833 p += TQPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
834 break;
835 case TQChar::Combining_AboveRight:
836 p += TQPoint( 0, -offset );
837 case TQChar::Combining_AboveRightAttached:
838 p += attachmentRect.topRight() - markRect.bottomRight();
839 break;
840
841 case TQChar::Combining_IotaSubscript:
842 default:
843 break;
844 }
845 //kdDebug() << "char=" << mark.unicode() << " combiningClass = " << cmb << " offset=" << p.x() << "/" << p.y() << endl;
846 markRect.moveBy( p.x(), p.y() );
847 p += TQPoint( -baseOffset, 0 );
848 attachmentRect |= markRect;
849 if ( boundingRect )
850 *boundingRect |= markRect;
851 lastCmb = cmb;
852 pa.setPoint( i, p );
853 }
854 return pa;
855}
856#endif
857
858//#define BIDI_DEBUG
859#ifdef BIDI_DEBUG
860#include <iostream>
861#endif
862
863static TQChar::Direction basicDirection(const TQString &str, int start = 0)
864{
865 int len = str.length();
866 int pos = start > len ? len -1 : start;
867 const TQChar *uc = str.unicode() + pos;
868 while( pos < len ) {
869 switch( uc->direction() )
870 {
871 case TQChar::DirL:
872 case TQChar::DirLRO:
873 case TQChar::DirLRE:
874 return TQChar::DirL;
875 case TQChar::DirR:
876 case TQChar::DirAL:
877 case TQChar::DirRLO:
878 case TQChar::DirRLE:
879 return TQChar::DirR;
880 default:
881 break;
882 }
883 ++pos;
884 ++uc;
885 }
886 if ( start != 0 )
887 return basicDirection( str );
888 return TQChar::DirL;
889}
890
891// transforms one line of the paragraph to visual order
892// the caller is responisble to delete the returned list of KoTextRuns.
893TQPtrList<KoTextRun> *KoComplexText::bidiReorderLine( KoBidiControl *control, const TQString &text, int start, int len,
894 TQChar::Direction basicDir )
895{
896 int last = start + len - 1;
897 //printf("doing BiDi reordering from %d to %d!\n", start, last);
898
899 TQPtrList<KoTextRun> *runs = new TQPtrList<KoTextRun>;
900 runs->setAutoDelete(true);
901
902 KoBidiContext *context = control->context;
903 if ( !context ) {
904 // first line
905 //if( start != 0 )
906 // kdDebug() << "bidiReorderLine::internal error" << endl;
907 if( basicDir == TQChar::DirR || (basicDir == TQChar::DirON && text.isRightToLeft() ) ) {
908 context = new KoBidiContext( 1, TQChar::DirR );
909 control->status.last = TQChar::DirR;
910 } else {
911 context = new KoBidiContext( 0, TQChar::DirL );
912 control->status.last = TQChar::DirL;
913 }
914 }
915
916 KoBidiStatus status = control->status;
917 TQChar::Direction dir = TQChar::DirON;
918
919 int sor = start;
920 int eor = start;
921
922 int current = start;
923 while(current <= last) {
924 TQChar::Direction dirCurrent;
925 if(current == (int)text.length()) {
926 KoBidiContext *c = context;
927 while ( c->parent )
928 c = c->parent;
929 dirCurrent = c->dir;
930 } else if ( current == last ) {
931 dirCurrent = ( basicDir != TQChar::DirON ? basicDir : basicDirection( text, current ) );
932 } else
933 dirCurrent = text.at(current).direction();
934
935
936#ifdef BIDI_DEBUG
937 cout << "directions: dir=" << dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << " level =" << (int)context->level << endl;
938#endif
939
940 switch(dirCurrent) {
941
942 // embedding and overrides (X1-X9 in the BiDi specs)
943 case TQChar::DirRLE:
944 {
945 uchar level = context->level;
946 if(level%2) // we have an odd level
947 level += 2;
948 else
949 level++;
950 if(level < 61) {
951 runs->append( new KoTextRun(sor, eor, context, dir) );
952 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
953 context = new KoBidiContext(level, TQChar::DirR, context);
954 status.last = TQChar::DirR;
955 status.lastStrong = TQChar::DirR;
956 }
957 break;
958 }
959 case TQChar::DirLRE:
960 {
961 uchar level = context->level;
962 if(level%2) // we have an odd level
963 level++;
964 else
965 level += 2;
966 if(level < 61) {
967 runs->append( new KoTextRun(sor, eor, context, dir) );
968 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
969 context = new KoBidiContext(level, TQChar::DirL, context);
970 status.last = TQChar::DirL;
971 status.lastStrong = TQChar::DirL;
972 }
973 break;
974 }
975 case TQChar::DirRLO:
976 {
977 uchar level = context->level;
978 if(level%2) // we have an odd level
979 level += 2;
980 else
981 level++;
982 if(level < 61) {
983 runs->append( new KoTextRun(sor, eor, context, dir) );
984 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
985 context = new KoBidiContext(level, TQChar::DirR, context, true);
986 dir = TQChar::DirR;
987 status.last = TQChar::DirR;
988 status.lastStrong = TQChar::DirR;
989 }
990 break;
991 }
992 case TQChar::DirLRO:
993 {
994 uchar level = context->level;
995 if(level%2) // we have an odd level
996 level++;
997 else
998 level += 2;
999 if(level < 61) {
1000 runs->append( new KoTextRun(sor, eor, context, dir) );
1001 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1002 context = new KoBidiContext(level, TQChar::DirL, context, true);
1003 dir = TQChar::DirL;
1004 status.last = TQChar::DirL;
1005 status.lastStrong = TQChar::DirL;
1006 }
1007 break;
1008 }
1009 case TQChar::DirPDF:
1010 {
1011 KoBidiContext *c = context->parent;
1012 if(c) {
1013 runs->append( new KoTextRun(sor, eor, context, dir) );
1014 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1015 status.last = context->dir;
1016 if( context->deref() ) delete context;
1017 context = c;
1018 if(context->override)
1019 dir = context->dir;
1020 else
1021 dir = TQChar::DirON;
1022 status.lastStrong = context->dir;
1023 }
1024 break;
1025 }
1026
1027 // strong types
1028 case TQChar::DirL:
1029 if(dir == TQChar::DirON)
1030 dir = TQChar::DirL;
1031 switch(status.last)
1032 {
1033 case TQChar::DirL:
1034 eor = current; status.eor = TQChar::DirL; break;
1035 case TQChar::DirR:
1036 case TQChar::DirAL:
1037 case TQChar::DirEN:
1038 case TQChar::DirAN:
1039 runs->append( new KoTextRun(sor, eor, context, dir) );
1040 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1041 break;
1042 case TQChar::DirES:
1043 case TQChar::DirET:
1044 case TQChar::DirCS:
1045 case TQChar::DirBN:
1046 case TQChar::DirB:
1047 case TQChar::DirS:
1048 case TQChar::DirWS:
1049 case TQChar::DirON:
1050 if(dir != TQChar::DirL) {
1051 //last stuff takes embedding dir
1052 if( context->dir == TQChar::DirR ) {
1053 if(status.eor != TQChar::DirR) {
1054 // AN or EN
1055 runs->append( new KoTextRun(sor, eor, context, dir) );
1056 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1057 dir = TQChar::DirR;
1058 }
1059 else
1060 eor = current - 1;
1061 runs->append( new KoTextRun(sor, eor, context, dir) );
1062 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1063 } else {
1064 if(status.eor != TQChar::DirL) {
1065 runs->append( new KoTextRun(sor, eor, context, dir) );
1066 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1067 dir = TQChar::DirL;
1068 } else {
1069 eor = current; status.eor = TQChar::DirL; break;
1070 }
1071 }
1072 } else {
1073 eor = current; status.eor = TQChar::DirL;
1074 }
1075 default:
1076 break;
1077 }
1078 status.lastStrong = TQChar::DirL;
1079 break;
1080 case TQChar::DirAL:
1081 case TQChar::DirR:
1082 if(dir == TQChar::DirON) dir = TQChar::DirR;
1083 switch(status.last)
1084 {
1085 case TQChar::DirR:
1086 case TQChar::DirAL:
1087 eor = current; status.eor = TQChar::DirR; break;
1088 case TQChar::DirL:
1089 case TQChar::DirEN:
1090 case TQChar::DirAN:
1091 runs->append( new KoTextRun(sor, eor, context, dir) );
1092 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1093 break;
1094 case TQChar::DirES:
1095 case TQChar::DirET:
1096 case TQChar::DirCS:
1097 case TQChar::DirBN:
1098 case TQChar::DirB:
1099 case TQChar::DirS:
1100 case TQChar::DirWS:
1101 case TQChar::DirON:
1102 if( status.eor != TQChar::DirR && status.eor != TQChar::DirAL ) {
1103 //last stuff takes embedding dir
1104 if(context->dir == TQChar::DirR || status.lastStrong == TQChar::DirR) {
1105 runs->append( new KoTextRun(sor, eor, context, dir) );
1106 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1107 dir = TQChar::DirR;
1108 eor = current;
1109 } else {
1110 eor = current - 1;
1111 runs->append( new KoTextRun(sor, eor, context, dir) );
1112 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1113 dir = TQChar::DirR;
1114 }
1115 } else {
1116 eor = current; status.eor = TQChar::DirR;
1117 }
1118 default:
1119 break;
1120 }
1121 status.lastStrong = dirCurrent;
1122 break;
1123
1124 // weak types:
1125
1126 case TQChar::DirNSM:
1127 // ### if @sor, set dir to dirSor
1128 break;
1129 case TQChar::DirEN:
1130 if(status.lastStrong != TQChar::DirAL) {
1131 // if last strong was AL change EN to AL
1132 if(dir == TQChar::DirON) {
1133 if(status.lastStrong == TQChar::DirL)
1134 dir = TQChar::DirL;
1135 else
1136 dir = TQChar::DirAN;
1137 }
1138 switch(status.last)
1139 {
1140 case TQChar::DirET:
1141 if ( status.lastStrong == TQChar::DirR || status.lastStrong == TQChar::DirAL ) {
1142 runs->append( new KoTextRun(sor, eor, context, dir) );
1143 ++eor; sor = eor; status.eor = TQChar::DirON;
1144 dir = TQChar::DirAN;
1145 }
1146 // fall through
1147 case TQChar::DirEN:
1148 case TQChar::DirL:
1149 eor = current;
1150 status.eor = dirCurrent;
1151 break;
1152 case TQChar::DirR:
1153 case TQChar::DirAL:
1154 case TQChar::DirAN:
1155 runs->append( new KoTextRun(sor, eor, context, dir) );
1156 ++eor; sor = eor; status.eor = TQChar::DirEN;
1157 dir = TQChar::DirAN; break;
1158 case TQChar::DirES:
1159 case TQChar::DirCS:
1160 if(status.eor == TQChar::DirEN || dir == TQChar::DirAN) {
1161 eor = current; break;
1162 }
1163 case TQChar::DirBN:
1164 case TQChar::DirB:
1165 case TQChar::DirS:
1166 case TQChar::DirWS:
1167 case TQChar::DirON:
1168 if(status.eor == TQChar::DirR) {
1169 // neutrals go to R
1170 eor = current - 1;
1171 runs->append( new KoTextRun(sor, eor, context, dir) );
1172 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirEN;
1173 dir = TQChar::DirAN;
1174 }
1175 else if( status.eor == TQChar::DirL ||
1176 (status.eor == TQChar::DirEN && status.lastStrong == TQChar::DirL)) {
1177 eor = current; status.eor = dirCurrent;
1178 } else {
1179 // numbers on both sides, neutrals get right to left direction
1180 if(dir != TQChar::DirL) {
1181 runs->append( new KoTextRun(sor, eor, context, dir) );
1182 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1183 eor = current - 1;
1184 dir = TQChar::DirR;
1185 runs->append( new KoTextRun(sor, eor, context, dir) );
1186 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1187 dir = TQChar::DirAN;
1188 } else {
1189 eor = current; status.eor = dirCurrent;
1190 }
1191 }
1192 default:
1193 break;
1194 }
1195 break;
1196 }
1197 case TQChar::DirAN:
1198 dirCurrent = TQChar::DirAN;
1199 if(dir == TQChar::DirON) dir = TQChar::DirAN;
1200 switch(status.last)
1201 {
1202 case TQChar::DirL:
1203 case TQChar::DirAN:
1204 eor = current; status.eor = TQChar::DirAN; break;
1205 case TQChar::DirR:
1206 case TQChar::DirAL:
1207 case TQChar::DirEN:
1208 runs->append( new KoTextRun(sor, eor, context, dir) );
1209 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1210 break;
1211 case TQChar::DirCS:
1212 if(status.eor == TQChar::DirAN) {
1213 eor = current; status.eor = TQChar::DirR; break;
1214 }
1215 case TQChar::DirES:
1216 case TQChar::DirET:
1217 case TQChar::DirBN:
1218 case TQChar::DirB:
1219 case TQChar::DirS:
1220 case TQChar::DirWS:
1221 case TQChar::DirON:
1222 if(status.eor == TQChar::DirR) {
1223 // neutrals go to R
1224 eor = current - 1;
1225 runs->append( new KoTextRun(sor, eor, context, dir) );
1226 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1227 dir = TQChar::DirAN;
1228 } else if( status.eor == TQChar::DirL ||
1229 (status.eor == TQChar::DirEN && status.lastStrong == TQChar::DirL)) {
1230 eor = current; status.eor = dirCurrent;
1231 } else {
1232 // numbers on both sides, neutrals get right to left direction
1233 if(dir != TQChar::DirL) {
1234 runs->append( new KoTextRun(sor, eor, context, dir) );
1235 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1236 eor = current - 1;
1237 dir = TQChar::DirR;
1238 runs->append( new KoTextRun(sor, eor, context, dir) );
1239 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1240 dir = TQChar::DirAN;
1241 } else {
1242 eor = current; status.eor = dirCurrent;
1243 }
1244 }
1245 default:
1246 break;
1247 }
1248 break;
1249 case TQChar::DirES:
1250 case TQChar::DirCS:
1251 break;
1252 case TQChar::DirET:
1253 if(status.last == TQChar::DirEN) {
1254 dirCurrent = TQChar::DirEN;
1255 eor = current; status.eor = dirCurrent;
1256 break;
1257 }
1258 break;
1259
1260 // boundary neutrals should be ignored
1261 case TQChar::DirBN:
1262 break;
1263 // neutrals
1264 case TQChar::DirB:
1265 // ### what do we do with newline and paragraph separators that come to here?
1266 break;
1267 case TQChar::DirS:
1268 // ### implement rule L1
1269 break;
1270 case TQChar::DirWS:
1271 case TQChar::DirON:
1272 break;
1273 default:
1274 break;
1275 }
1276
1277 //cout << " after: dir=" << // dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << endl;
1278
1279 if(current >= (int)text.length()) break;
1280
1281 // set status.last as needed.
1282 switch(dirCurrent)
1283 {
1284 case TQChar::DirET:
1285 case TQChar::DirES:
1286 case TQChar::DirCS:
1287 case TQChar::DirS:
1288 case TQChar::DirWS:
1289 case TQChar::DirON:
1290 switch(status.last)
1291 {
1292 case TQChar::DirL:
1293 case TQChar::DirR:
1294 case TQChar::DirAL:
1295 case TQChar::DirEN:
1296 case TQChar::DirAN:
1297 status.last = dirCurrent;
1298 break;
1299 default:
1300 status.last = TQChar::DirON;
1301 }
1302 break;
1303 case TQChar::DirNSM:
1304 case TQChar::DirBN:
1305 // ignore these
1306 break;
1307 default:
1308 status.last = dirCurrent;
1309 }
1310
1311 ++current;
1312 }
1313
1314#ifdef BIDI_DEBUG
1315 cout << "reached end of line current=" << current << ", eor=" << eor << endl;
1316#endif
1317 eor = current - 1; // remove dummy char
1318
1319 if ( sor <= eor )
1320 runs->append( new KoTextRun(sor, eor, context, dir) );
1321
1322 // reorder line according to run structure...
1323
1324 // first find highest and lowest levels
1325 uchar levelLow = 128;
1326 uchar levelHigh = 0;
1327 KoTextRun *r = runs->first();
1328 while ( r ) {
1329 //printf("level = %d\n", r->level);
1330 if ( r->level > levelHigh )
1331 levelHigh = r->level;
1332 if ( r->level < levelLow )
1333 levelLow = r->level;
1334 r = runs->next();
1335 }
1336
1337 // implements reordering of the line (L2 according to BiDi spec):
1338 // L2. From the highest level found in the text to the lowest odd level on each line,
1339 // reverse any contiguous sequence of characters that are at that level or higher.
1340
1341 // reversing is only done up to the lowest odd level
1342 if(!(levelLow%2)) levelLow++;
1343
1344#ifdef BIDI_DEBUG
1345 cout << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
1346 cout << "logical order is:" << endl;
1347 TQPtrListIterator<KoTextRun> it2(*runs);
1348 KoTextRun *r2;
1349 for ( ; (r2 = it2.current()); ++it2 )
1350 cout << " " << r2 << " start=" << r2->start << " stop=" << r2->stop << " level=" << (uint)r2->level << endl;
1351#endif
1352
1353 int count = runs->count() - 1;
1354
1355 while(levelHigh >= levelLow)
1356 {
1357 int i = 0;
1358 while ( i < count )
1359 {
1360 while(i < count && runs->at(i)->level < levelHigh) i++;
1361 int start = i;
1362 while(i <= count && runs->at(i)->level >= levelHigh) i++;
1363 int end = i-1;
1364
1365 if(start != end)
1366 {
1367 //cout << "reversing from " << start << " to " << end << endl;
1368 for(int j = 0; j < (end-start+1)/2; j++)
1369 {
1370 KoTextRun *first = runs->take(start+j);
1371 KoTextRun *last = runs->take(end-j-1);
1372 runs->insert(start+j, last);
1373 runs->insert(end-j, first);
1374 }
1375 }
1376 i++;
1377 if(i >= count) break;
1378 }
1379 levelHigh--;
1380 }
1381
1382#ifdef BIDI_DEBUG
1383 cout << "visual order is:" << endl;
1384 TQPtrListIterator<KoTextRun> it3(*runs);
1385 KoTextRun *r3;
1386 for ( ; (r3 = it3.current()); ++it3 )
1387 {
1388 cout << " " << r3 << endl;
1389 }
1390#endif
1391
1392 control->setContext( context );
1393 control->status = status;
1394
1395 return runs;
1396}
1397
1398
1399TQString KoComplexText::bidiReorderString( const TQString &str, TQChar::Direction /*basicDir*/ )
1400{
1401
1402// ### fix basic direction
1403 KoBidiControl control;
1404 int lineStart = 0;
1405 int lineEnd = 0;
1406 int len = str.length();
1407 TQString visual;
1408 visual.setUnicode( 0, len );
1409 TQChar *vch = (TQChar *)visual.unicode();
1410 const TQChar *ch = str.unicode();
1411 while( lineStart < len ) {
1412 lineEnd = lineStart;
1413 while( *ch != '\n' && lineEnd < len ) {
1414 ch++;
1415 lineEnd++;
1416 }
1417 lineEnd++;
1418 TQPtrList<KoTextRun> *runs = bidiReorderLine( &control, str, lineStart, lineEnd - lineStart );
1419
1420 // reorder the content of the line, and output to visual
1421 KoTextRun *r = runs->first();
1422 while ( r ) {
1423 if(r->level %2) {
1424 // odd level, need to reverse the string
1425 int pos = r->stop;
1426 while(pos >= r->start) {
1427 *vch = str[pos];
1428 if ( vch->mirrored() )
1429 *vch = vch->mirroredChar();
1430 vch++;
1431 pos--;
1432 }
1433 } else {
1434 int pos = r->start;
1435 while(pos <= r->stop) {
1436 *vch = str[pos];
1437 vch++;
1438 pos++;
1439 }
1440 }
1441 r = runs->next();
1442 }
1443 if ( *ch == '\n' ) {
1444 *vch = *ch;
1445 vch++;
1446 ch++;
1447 lineEnd++;
1448 }
1449 lineStart = lineEnd;
1450 }
1451 return visual;
1452}
1453
1454KoTextRun::KoTextRun(int _start, int _stop, KoBidiContext *context, TQChar::Direction dir) {
1455 start = _start;
1456 stop = _stop;
1457 if(dir == TQChar::DirON) dir = context->dir;
1458
1459 level = context->level;
1460
1461 // add level of run (cases I1 & I2)
1462 if( level % 2 ) {
1463 if(dir == TQChar::DirL || dir == TQChar::DirAN)
1464 level++;
1465 } else {
1466 if( dir == TQChar::DirR )
1467 level++;
1468 else if( dir == TQChar::DirAN )
1469 level += 2;
1470 }
1471#ifdef BIDI_DEBUG
1472 printf("new run: dir=%d from %d, to %d level = %d\n", dir, _start, _stop, level);
1473#endif
1474}
1475
1476#endif //TQT_NO_COMPLEXTEXT
TDEAction * next(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)