39#ifndef TQT_NO_COMPLEXTEXT
40#include "KoRichText.h"
42#include "tqfontmetrics.h"
53KoBidiContext::KoBidiContext( uchar l, TQChar::Direction e, KoBidiContext *p, bool o )
54 : level(l) , override(o), dir(e)
62KoBidiContext::~KoBidiContext()
64 if( parent && parent->deref() )
68static TQChar *shapeBuffer = 0;
69static int shapeBufSize = 0;
112static inline const TQChar *prevChar( const TQString &str, int pos )
116 const TQChar *ch = str.unicode() + pos;
123 return &TQChar::replacement;
126static inline const TQChar *nextChar( const TQString &str, int pos)
129 int len = str.length();
130 const TQChar *ch = str.unicode() + pos;
139 return &TQChar::replacement;
142static inline bool prevVisualCharJoins( const TQString &str, int pos)
144 return ( prevChar( str, pos )->joining() != TQChar::OtherJoining );
147static inline bool nextVisualCharJoins( const TQString &str, int pos)
149 TQChar::Joining join = nextChar( str, pos )->joining();
150 return ( join == TQChar::Dual || join == TQChar::Center );
154KoComplexText::Shape KoComplexText::glyphVariant( const TQString &str, int pos)
157 TQChar::Joining joining = str[pos].joining();
160 case TQChar::OtherJoining:
166 if( nextVisualCharJoins( str, pos ) )
170 bool right = nextVisualCharJoins( str, pos );
171 bool left = prevVisualCharJoins( str, pos );
187static inline bool prevLogicalCharJoins( const TQString &str, int pos)
189 return ( nextChar( str, pos )->joining() != TQChar::OtherJoining );
192static inline bool nextLogicalCharJoins( const TQString &str, int pos)
194 TQChar::Joining join = prevChar( str, pos )->joining();
195 return ( join == TQChar::Dual || join == TQChar::Center );
199KoComplexText::Shape KoComplexText::glyphVariantLogical( const TQString &str, int pos)
202 TQChar::Joining joining = str[pos].joining();
205 case TQChar::OtherJoining:
211 if( nextLogicalCharJoins( str, pos ) )
215 bool right = nextLogicalCharJoins( str, pos );
216 bool left = prevLogicalCharJoins( str, pos );
235static const ushort arabicUnicodeMapping[256][2] = {
518static const ushort arabicUnicodeLamAlefMapping[6][4] = {
519 { 0xfffd, 0xfffd, 0xfef5, 0xfef6 },
520 { 0xfffd, 0xfffd, 0xfef7, 0xfef8 },
521 { 0xfffd, 0xfffd, 0xfffd, 0xfffd },
522 { 0xfffd, 0xfffd, 0xfef9, 0xfefa },
523 { 0xfffd, 0xfffd, 0xfffd, 0xfffd },
524 { 0xfffd, 0xfffd, 0xfefb, 0xfefc }
527static inline int getShape( const TQChar * , uchar cell, int shape,
528 const TQFontMetrics * )
530 uint ch = arabicUnicodeMapping[cell][0] + shape;
554TQString KoComplexText::shapedString( const TQString& uc, int from, int len, TQPainter::TextDirection dir, const TQFontMetrics *fm )
557 len = uc.length() - from;
563 int num = uc.length() - from - len;
564 const TQChar *ch = uc.unicode() + from + len;
565 while ( num > 0 && ch->combiningClass() != 0 ) {
570 ch = uc.unicode() + from;
571 while ( len > 0 && ch->combiningClass() != 0 ) {
576 if ( len == 0 ) return TQString();
578 if( !shapeBuffer || len > shapeBufSize ) {
579 if( shapeBuffer ) free( ( void *) shapeBuffer );
580 shapeBuffer = (TQChar *) malloc( len* sizeof( TQChar ) );
587 TQChar *data = shapeBuffer;
588 if ( dir == TQPainter::RTL )
590 for ( int i = 0; i < len; i++ ) {
592 uchar c = ch->cell();
594 if ( dir == TQPainter::RTL && ch->mirrored() )
595 *data = ch->mirroredChar();
602 if ( dir == TQPainter::RTL )
603 pos = from + len - 1 - i;
604 int shape = glyphVariantLogical( uc, pos );
610 const TQChar *pch = nextChar( uc, pos );
611 if ( pch->row() == 0x06 ) {
612 switch ( pch->cell() ) {
618 map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
630 if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
638 map = getShape( ch, c, shape, fm );
645 if ( dir == TQPainter::RTL )
651 if ( dir == TQPainter::Auto && !uc.simpleText() ) {
652 return bidiReorderString( TQConstString( shapeBuffer, lenOut ). string() );
654 if ( dir == TQPainter::RTL ) {
656 TQChar *s = shapeBuffer;
658 while ( i < lenOut ) {
659 if ( s->combiningClass() != 0 ) {
666 } while ( ch->combiningClass() != 0 );
670 while ( j < clen/2 ) {
687 return TQConstString( shapeBuffer, lenOut ).string();
690TQChar KoComplexText::shapedCharacter( const TQString &str, int pos, const TQFontMetrics *fm )
692 const TQChar *ch = str.unicode() + pos;
693 if ( ch->row() != 0x06 )
696 int shape = glyphVariantLogical( str, pos );
699 switch ( ch->cell() ) {
701 const TQChar *nch = nextChar( str, pos );
702 if ( nch->row() == 0x06 ) {
703 switch ( nch->cell() ) {
708 return TQChar(arabicUnicodeLamAlefMapping[nch->cell() - 0x22][shape]);
719 if ( prevChar( str, pos )->unicode() == 0x0644 )
725 return TQChar( getShape( ch, ch->cell(), shape, fm ) );
731TQPointArray KoComplexText::positionMarks( TQFontPrivate *f, const TQString &str,
732 int pos, TQRect *boundingRect )
734 int len = str.length();
736 while ( pos + nmarks < len && str[pos+nmarks +1].combiningClass() > 0 )
740 return TQPointArray();
742 TQChar baseChar = KoComplexText::shapedCharacter( str, pos );
743 TQRect baseRect = f->boundingRect( baseChar );
744 int baseOffset = f->textWidth( str, pos, 1 );
747 int offset = f->actual.pixelSize / 10 + 1;
749 TQPointArray pa( nmarks );
751 unsigned char lastCmb = 0;
752 TQRect attachmentRect;
754 *boundingRect = baseRect;
755 for( i = 0; i < nmarks; i++ ) {
756 TQChar mark = str[pos+i+1];
757 unsigned char cmb = mark.combiningClass();
763 if ( cmb >= 27 && cmb <= 36 )
766 if ( (cmb >= 10 && cmb <= 18) ||
767 cmb == 20 || cmb == 22 ||
768 cmb == 29 || cmb == 32 )
769 cmb = TQChar::Combining_Below;
771 else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
772 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
773 cmb = TQChar::Combining_Above;
775 else if ( cmb == 103 )
776 cmb = TQChar::Combining_BelowRight;
778 else if ( cmb == 24 || cmb == 107 )
779 cmb = TQChar::Combining_AboveRight;
780 else if ( cmb == 25 )
781 cmb = TQChar::Combining_AboveLeft;
788 if ( cmb != lastCmb ) {
790 attachmentRect = baseRect;
794 TQRect markRect = f->boundingRect( mark );
796 case TQChar::Combining_DoubleBelow:
798 case TQChar::Combining_BelowLeft:
799 p += TQPoint( 0, offset );
800 case TQChar::Combining_BelowLeftAttached:
801 p += attachmentRect.bottomLeft() - markRect.topLeft();
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 );
809 case TQChar::Combining_BelowRight:
810 p += TQPoint( 0, offset );
811 case TQChar::Combining_BelowRightAttached:
812 p += attachmentRect.bottomRight() - markRect.topRight();
814 case TQChar::Combining_Left:
815 p += TQPoint( -offset, 0 );
816 case TQChar::Combining_LeftAttached:
818 case TQChar::Combining_Right:
819 p += TQPoint( offset, 0 );
820 case TQChar::Combining_RightAttached:
822 case TQChar::Combining_DoubleAbove:
824 case TQChar::Combining_AboveLeft:
825 p += TQPoint( 0, -offset );
826 case TQChar::Combining_AboveLeftAttached:
827 p += attachmentRect.topLeft() - markRect.bottomLeft();
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 );
835 case TQChar::Combining_AboveRight:
836 p += TQPoint( 0, -offset );
837 case TQChar::Combining_AboveRightAttached:
838 p += attachmentRect.topRight() - markRect.bottomRight();
841 case TQChar::Combining_IotaSubscript:
846 markRect.moveBy( p.x(), p.y() );
847 p += TQPoint( -baseOffset, 0 );
848 attachmentRect |= markRect;
850 *boundingRect |= markRect;
863static TQChar::Direction basicDirection( const TQString &str, int start = 0)
865 int len = str.length();
866 int pos = start > len ? len -1 : start;
867 const TQChar *uc = str.unicode() + pos;
869 switch( uc->direction() )
887 return basicDirection( str );
893TQPtrList<KoTextRun> *KoComplexText::bidiReorderLine( KoBidiControl *control, const TQString &text, int start, int len,
894 TQChar::Direction basicDir )
896 int last = start + len - 1;
899 TQPtrList<KoTextRun> *runs = new TQPtrList<KoTextRun>;
900 runs->setAutoDelete( true);
902 KoBidiContext *context = control->context;
907 if( basicDir == TQChar::DirR || (basicDir == TQChar::DirON && text.isRightToLeft() ) ) {
908 context = new KoBidiContext( 1, TQChar::DirR );
909 control->status.last = TQChar::DirR;
911 context = new KoBidiContext( 0, TQChar::DirL );
912 control->status.last = TQChar::DirL;
916 KoBidiStatus status = control->status;
917 TQChar::Direction dir = TQChar::DirON;
923 while(current <= last) {
924 TQChar::Direction dirCurrent;
925 if(current == ( int)text.length()) {
926 KoBidiContext *c = context;
930 } else if ( current == last ) {
931 dirCurrent = ( basicDir != TQChar::DirON ? basicDir : basicDirection( text, current ) );
933 dirCurrent = text.at(current).direction();
937 cout << "directions: dir=" << dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << context->dir << " level =" << (int)context->level << endl;
945 uchar level = context->level;
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;
961 uchar level = context->level;
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;
977 uchar level = context->level;
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);
987 status.last = TQChar::DirR;
988 status.lastStrong = TQChar::DirR;
994 uchar level = context->level;
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);
1004 status.last = TQChar::DirL;
1005 status.lastStrong = TQChar::DirL;
1009 case TQChar::DirPDF:
1011 KoBidiContext *c = context->parent;
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;
1018 if(context->override)
1021 dir = TQChar::DirON;
1022 status.lastStrong = context->dir;
1029 if(dir == TQChar::DirON)
1034 eor = current; status.eor = TQChar::DirL; break;
1039 runs->append( new KoTextRun(sor, eor, context, dir) );
1040 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1050 if(dir != TQChar::DirL) {
1052 if( context->dir == TQChar::DirR ) {
1053 if(status.eor != TQChar::DirR) {
1055 runs->append( new KoTextRun(sor, eor, context, dir) );
1056 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1061 runs->append( new KoTextRun(sor, eor, context, dir) );
1062 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
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;
1069 eor = current; status.eor = TQChar::DirL; break;
1073 eor = current; status.eor = TQChar::DirL;
1078 status.lastStrong = TQChar::DirL;
1082 if(dir == TQChar::DirON) dir = TQChar::DirR;
1087 eor = current; status.eor = TQChar::DirR; break;
1091 runs->append( new KoTextRun(sor, eor, context, dir) );
1092 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1102 if( status.eor != TQChar::DirR && status.eor != TQChar::DirAL ) {
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;
1111 runs->append( new KoTextRun(sor, eor, context, dir) );
1112 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1116 eor = current; status.eor = TQChar::DirR;
1121 status.lastStrong = dirCurrent;
1126 case TQChar::DirNSM:
1130 if(status.lastStrong != TQChar::DirAL) {
1132 if(dir == TQChar::DirON) {
1133 if(status.lastStrong == TQChar::DirL)
1136 dir = TQChar::DirAN;
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;
1150 status.eor = dirCurrent;
1155 runs->append( new KoTextRun(sor, eor, context, dir) );
1156 ++eor; sor = eor; status.eor = TQChar::DirEN;
1157 dir = TQChar::DirAN; break;
1160 if(status.eor == TQChar::DirEN || dir == TQChar::DirAN) {
1161 eor = current; break;
1168 if(status.eor == TQChar::DirR) {
1171 runs->append( new KoTextRun(sor, eor, context, dir) );
1172 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirEN;
1173 dir = TQChar::DirAN;
1175 else if( status.eor == TQChar::DirL ||
1176 (status.eor == TQChar::DirEN && status.lastStrong == TQChar::DirL)) {
1177 eor = current; status.eor = dirCurrent;
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;
1185 runs->append( new KoTextRun(sor, eor, context, dir) );
1186 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1187 dir = TQChar::DirAN;
1189 eor = current; status.eor = dirCurrent;
1198 dirCurrent = TQChar::DirAN;
1199 if(dir == TQChar::DirON) dir = TQChar::DirAN;
1204 eor = current; status.eor = TQChar::DirAN; break;
1208 runs->append( new KoTextRun(sor, eor, context, dir) );
1209 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1212 if(status.eor == TQChar::DirAN) {
1213 eor = current; status.eor = TQChar::DirR; break;
1222 if(status.eor == TQChar::DirR) {
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;
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;
1238 runs->append( new KoTextRun(sor, eor, context, dir) );
1239 ++eor; sor = eor; dir = TQChar::DirON; status.eor = TQChar::DirON;
1240 dir = TQChar::DirAN;
1242 eor = current; status.eor = dirCurrent;
1253 if(status.last == TQChar::DirEN) {
1254 dirCurrent = TQChar::DirEN;
1255 eor = current; status.eor = dirCurrent;
1279 if(current >= ( int)text.length()) break;
1297 status.last = dirCurrent;
1300 status.last = TQChar::DirON;
1303 case TQChar::DirNSM:
1308 status.last = dirCurrent;
1315 cout << "reached end of line current=" << current << ", eor=" << eor << endl;
1320 runs->append( new KoTextRun(sor, eor, context, dir) );
1325 uchar levelLow = 128;
1326 uchar levelHigh = 0;
1327 KoTextRun *r = runs->first();
1330 if ( r->level > levelHigh )
1331 levelHigh = r->level;
1332 if ( r->level < levelLow )
1333 levelLow = r->level;
1342 if(!(levelLow%2)) levelLow++;
1345 cout << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
1346 cout << "logical order is:" << endl;
1347 TQPtrListIterator<KoTextRun> it2(*runs);
1349 for ( ; (r2 = it2.current()); ++it2 )
1350 cout << " " << r2 << " start=" << r2->start << " stop=" << r2->stop << " level=" << (uint)r2->level << endl;
1353 int count = runs->count() - 1;
1355 while(levelHigh >= levelLow)
1360 while(i < count && runs->at(i)->level < levelHigh) i++;
1362 while(i <= count && runs->at(i)->level >= levelHigh) i++;
1368 for( int j = 0; j < (end-start+1)/2; j++)
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);
1377 if(i >= count) break;
1383 cout << "visual order is:" << endl;
1384 TQPtrListIterator<KoTextRun> it3(*runs);
1386 for ( ; (r3 = it3.current()); ++it3 )
1388 cout << " " << r3 << endl;
1392 control->setContext( context );
1393 control->status = status;
1399TQString KoComplexText::bidiReorderString( const TQString &str, TQChar::Direction )
1403 KoBidiControl control;
1406 int len = str.length();
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 ) {
1418 TQPtrList<KoTextRun> *runs = bidiReorderLine( &control, str, lineStart, lineEnd - lineStart );
1421 KoTextRun *r = runs->first();
1426 while(pos >= r->start) {
1428 if ( vch->mirrored() )
1429 *vch = vch->mirroredChar();
1435 while(pos <= r->stop) {
1443 if ( *ch == '\n' ) {
1449 lineStart = lineEnd;
1454KoTextRun::KoTextRun( int _start, int _stop, KoBidiContext *context, TQChar::Direction dir) {
1457 if(dir == TQChar::DirON) dir = context->dir;
1459 level = context->level;
1463 if(dir == TQChar::DirL || dir == TQChar::DirAN)
1466 if( dir == TQChar::DirR )
1468 else if( dir == TQChar::DirAN )
1472 printf( "new run: dir=%d from %d, to %d level = %d\n", dir, _start, _stop, level);
TDEAction * next(const TQObject *recvr, const char *slot, TDEActionCollection *parent, const char *name=0)
|