[2] | 1 | /****************************************************************************
|
---|
| 2 | ** $Id: qtextengine.cpp 8 2005-11-16 19:36:46Z dmik $
|
---|
| 3 | **
|
---|
| 4 | ** Text engine classes
|
---|
| 5 | **
|
---|
| 6 | ** Copyright (C) 2003 Trolltech AS. All rights reserved.
|
---|
| 7 | **
|
---|
| 8 | ** This file is part of the kernel module of the Qt GUI Toolkit.
|
---|
| 9 | **
|
---|
| 10 | ** This file may be distributed under the terms of the Q Public License
|
---|
| 11 | ** as defined by Trolltech AS of Norway and appearing in the file
|
---|
| 12 | ** LICENSE.QPL included in the packaging of this file.
|
---|
| 13 | **
|
---|
| 14 | ** This file may be distributed and/or modified under the terms of the
|
---|
| 15 | ** GNU General Public License version 2 as published by the Free Software
|
---|
| 16 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
| 17 | ** packaging of this file.
|
---|
| 18 | **
|
---|
| 19 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
| 20 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
| 21 | ** Agreement provided with the Software.
|
---|
| 22 | **
|
---|
| 23 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
| 24 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
| 25 | **
|
---|
| 26 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
| 27 | ** information about Qt Commercial License Agreements.
|
---|
| 28 | ** See http://www.trolltech.com/qpl/ for QPL licensing information.
|
---|
| 29 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
| 30 | **
|
---|
| 31 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
| 32 | ** not clear to you.
|
---|
| 33 | **
|
---|
| 34 | **********************************************************************/
|
---|
| 35 |
|
---|
| 36 | #include "qtextengine_p.h"
|
---|
| 37 |
|
---|
| 38 | #include "qscriptengine_p.h"
|
---|
| 39 | #include <qfont.h>
|
---|
| 40 | #include "qfontdata_p.h"
|
---|
| 41 | #include "qfontengine_p.h"
|
---|
| 42 | #include <qstring.h>
|
---|
| 43 | #include <private/qunicodetables_p.h>
|
---|
| 44 | #include <stdlib.h>
|
---|
| 45 |
|
---|
| 46 | // -----------------------------------------------------------------------------------------------------
|
---|
| 47 | //
|
---|
| 48 | // The BiDi algorithm
|
---|
| 49 | //
|
---|
| 50 | // -----------------------------------------------------------------------------------------------------
|
---|
| 51 |
|
---|
| 52 |
|
---|
| 53 | #define BIDI_DEBUG 0//2
|
---|
| 54 | #if (BIDI_DEBUG >= 1)
|
---|
| 55 | #include <iostream>
|
---|
| 56 |
|
---|
| 57 | static const char *directions[] = {
|
---|
| 58 | "DirL", "DirR", "DirEN", "DirES", "DirET", "DirAN", "DirCS", "DirB", "DirS", "DirWS", "DirON",
|
---|
| 59 | "DirLRE", "DirLRO", "DirAL", "DirRLE", "DirRLO", "DirPDF", "DirNSM", "DirBN"
|
---|
| 60 | };
|
---|
| 61 |
|
---|
| 62 | #endif
|
---|
| 63 |
|
---|
| 64 | struct BidiStatus {
|
---|
| 65 | BidiStatus() {
|
---|
| 66 | eor = QChar::DirON;
|
---|
| 67 | lastStrong = QChar::DirON;
|
---|
| 68 | last = QChar:: DirON;
|
---|
| 69 | dir = QChar::DirON;
|
---|
| 70 | }
|
---|
| 71 | QChar::Direction eor;
|
---|
| 72 | QChar::Direction lastStrong;
|
---|
| 73 | QChar::Direction last;
|
---|
| 74 | QChar::Direction dir;
|
---|
| 75 | };
|
---|
| 76 |
|
---|
| 77 | struct BidiControl {
|
---|
| 78 | struct Context {
|
---|
| 79 | unsigned char level : 6;
|
---|
| 80 | unsigned char override : 1;
|
---|
| 81 | unsigned char unused : 1;
|
---|
| 82 | };
|
---|
| 83 |
|
---|
| 84 | inline BidiControl( bool rtl )
|
---|
| 85 | : cCtx( 0 ), singleLine( FALSE ) {
|
---|
| 86 | ctx[0].level = (rtl ? 1 : 0);
|
---|
| 87 | ctx[0].override = FALSE;
|
---|
| 88 | }
|
---|
| 89 |
|
---|
| 90 | inline void embed( int level, bool override = FALSE ) {
|
---|
| 91 | if ( ctx[cCtx].level < 61 && cCtx < 61 ) {
|
---|
| 92 | (void) ++cCtx;
|
---|
| 93 | ctx[cCtx].level = level;
|
---|
| 94 | ctx[cCtx].override = override;
|
---|
| 95 | }
|
---|
| 96 | }
|
---|
| 97 | inline void pdf() {
|
---|
| 98 | if ( cCtx ) (void) --cCtx;
|
---|
| 99 | }
|
---|
| 100 |
|
---|
| 101 | inline uchar level() const {
|
---|
| 102 | return ctx[cCtx].level;
|
---|
| 103 | }
|
---|
| 104 | inline bool override() const {
|
---|
| 105 | return ctx[cCtx].override;
|
---|
| 106 | }
|
---|
| 107 | inline QChar::Direction basicDirection() {
|
---|
| 108 | return (ctx[0].level ? QChar::DirR : QChar:: DirL );
|
---|
| 109 | }
|
---|
| 110 | inline uchar baseLevel() {
|
---|
| 111 | return ctx[0].level;
|
---|
| 112 | }
|
---|
| 113 | inline QChar::Direction direction() {
|
---|
| 114 | return ((ctx[cCtx].level%2) ? QChar::DirR : QChar:: DirL );
|
---|
| 115 | }
|
---|
| 116 |
|
---|
| 117 | Context ctx[63];
|
---|
| 118 | unsigned int cCtx : 8;
|
---|
| 119 | bool singleLine : 8;
|
---|
| 120 | };
|
---|
| 121 |
|
---|
| 122 | static QChar::Direction basicDirection( const QString &str )
|
---|
| 123 | {
|
---|
| 124 | int len = str.length();
|
---|
| 125 | int pos = 0;
|
---|
| 126 | const QChar *uc = str.unicode() + pos;
|
---|
| 127 | while( pos < len ) {
|
---|
| 128 | switch( direction( *uc ) )
|
---|
| 129 | {
|
---|
| 130 | case QChar::DirL:
|
---|
| 131 | case QChar::DirLRO:
|
---|
| 132 | case QChar::DirLRE:
|
---|
| 133 | return QChar::DirL;
|
---|
| 134 | case QChar::DirR:
|
---|
| 135 | case QChar::DirAL:
|
---|
| 136 | case QChar::DirRLO:
|
---|
| 137 | case QChar::DirRLE:
|
---|
| 138 | return QChar::DirR;
|
---|
| 139 | default:
|
---|
| 140 | break;
|
---|
| 141 | }
|
---|
| 142 | ++pos;
|
---|
| 143 | ++uc;
|
---|
| 144 | }
|
---|
| 145 | return QChar::DirL;
|
---|
| 146 | }
|
---|
| 147 |
|
---|
| 148 |
|
---|
| 149 | static void qAppendItems(QTextEngine *engine, int &start, int &stop, BidiControl &control, QChar::Direction dir )
|
---|
| 150 | {
|
---|
| 151 | QScriptItemArray &items = engine->items;
|
---|
| 152 | const QChar *text = engine->string.unicode();
|
---|
| 153 |
|
---|
| 154 | if ( start > stop ) {
|
---|
| 155 | // #### the algorithm is currently not really safe against this. Still needs fixing.
|
---|
| 156 | // qWarning( "Bidi: appendItems() internal error" );
|
---|
| 157 | return;
|
---|
| 158 | }
|
---|
| 159 |
|
---|
| 160 | int level = control.level();
|
---|
| 161 |
|
---|
| 162 | if(dir != QChar::DirON && !control.override()) {
|
---|
| 163 | // add level of run (cases I1 & I2)
|
---|
| 164 | if( level % 2 ) {
|
---|
| 165 | if(dir == QChar::DirL || dir == QChar::DirAN || dir == QChar::DirEN )
|
---|
| 166 | level++;
|
---|
| 167 | } else {
|
---|
| 168 | if( dir == QChar::DirR )
|
---|
| 169 | level++;
|
---|
| 170 | else if( dir == QChar::DirAN || dir == QChar::DirEN )
|
---|
| 171 | level += 2;
|
---|
| 172 | }
|
---|
| 173 | }
|
---|
| 174 |
|
---|
| 175 | #if (BIDI_DEBUG >= 1)
|
---|
| 176 | qDebug("new run: dir=%s from %d, to %d level = %d\n", directions[dir], start, stop, level);
|
---|
| 177 | #endif
|
---|
| 178 | QFont::Script script = QFont::NoScript;
|
---|
| 179 | QScriptItem item;
|
---|
| 180 | item.position = start;
|
---|
| 181 | item.analysis.script = script;
|
---|
| 182 | item.analysis.bidiLevel = level;
|
---|
| 183 | item.analysis.override = control.override();
|
---|
| 184 | item.analysis.reserved = 0;
|
---|
| 185 |
|
---|
| 186 | if ( control.singleLine ) {
|
---|
| 187 | for ( int i = start; i <= stop; i++ ) {
|
---|
| 188 |
|
---|
| 189 | unsigned short uc = text[i].unicode();
|
---|
| 190 | QFont::Script s = (QFont::Script)scriptForChar( uc );
|
---|
| 191 | if (s == QFont::UnknownScript || s == QFont::CombiningMarks)
|
---|
| 192 | s = script;
|
---|
| 193 |
|
---|
| 194 | if (s != script) {
|
---|
| 195 | item.analysis.script = s;
|
---|
| 196 | item.analysis.bidiLevel = level;
|
---|
| 197 | item.position = i;
|
---|
| 198 | items.append( item );
|
---|
| 199 | script = s;
|
---|
| 200 | }
|
---|
| 201 | }
|
---|
| 202 | } else {
|
---|
| 203 | for ( int i = start; i <= stop; i++ ) {
|
---|
| 204 |
|
---|
| 205 | unsigned short uc = text[i].unicode();
|
---|
| 206 | QFont::Script s = (QFont::Script)scriptForChar( uc );
|
---|
| 207 | if (s == QFont::UnknownScript || s == QFont::CombiningMarks)
|
---|
| 208 | s = script;
|
---|
| 209 |
|
---|
| 210 | QChar::Category category = ::category( uc );
|
---|
| 211 | if ( uc == 0xfffcU || uc == 0x2028U ) {
|
---|
| 212 | item.analysis.bidiLevel = level % 2 ? level-1 : level;
|
---|
| 213 | item.analysis.script = QFont::Latin;
|
---|
| 214 | item.isObject = TRUE;
|
---|
| 215 | s = QFont::NoScript;
|
---|
| 216 | } else if ((uc >= 9 && uc <=13) ||
|
---|
| 217 | (category >= QChar::Separator_Space && category <= QChar::Separator_Paragraph)) {
|
---|
| 218 | item.analysis.script = QFont::Latin;
|
---|
| 219 | item.isSpace = TRUE;
|
---|
| 220 | item.isTab = ( uc == '\t' );
|
---|
| 221 | item.analysis.bidiLevel = item.isTab ? control.baseLevel() : level;
|
---|
| 222 | s = QFont::NoScript;
|
---|
| 223 | } else if ( s != script && (category != QChar::Mark_NonSpacing || script == QFont::NoScript)) {
|
---|
| 224 | item.analysis.script = s;
|
---|
| 225 | item.analysis.bidiLevel = level;
|
---|
| 226 | } else {
|
---|
| 227 | continue;
|
---|
| 228 | }
|
---|
| 229 |
|
---|
| 230 | item.position = i;
|
---|
| 231 | items.append( item );
|
---|
| 232 | script = s;
|
---|
| 233 | item.isSpace = item.isTab = item.isObject = FALSE;
|
---|
| 234 | }
|
---|
| 235 | }
|
---|
| 236 | ++stop;
|
---|
| 237 | start = stop;
|
---|
| 238 | }
|
---|
| 239 |
|
---|
| 240 | typedef void (* fAppendItems)(QTextEngine *, int &start, int &stop, BidiControl &control, QChar::Direction dir);
|
---|
| 241 | static fAppendItems appendItems = qAppendItems;
|
---|
| 242 |
|
---|
| 243 | // creates the next QScript items.
|
---|
| 244 | static void bidiItemize( QTextEngine *engine, bool rightToLeft, int mode )
|
---|
| 245 | {
|
---|
| 246 | BidiControl control( rightToLeft );
|
---|
| 247 | if ( mode & QTextEngine::SingleLine )
|
---|
| 248 | control.singleLine = TRUE;
|
---|
| 249 |
|
---|
| 250 | int sor = 0;
|
---|
| 251 | int eor = -1;
|
---|
| 252 |
|
---|
| 253 | // ### should get rid of this!
|
---|
| 254 | bool first = TRUE;
|
---|
| 255 |
|
---|
| 256 | int length = engine->string.length();
|
---|
| 257 |
|
---|
| 258 | if ( !length )
|
---|
| 259 | return;
|
---|
| 260 |
|
---|
| 261 | const QChar *unicode = engine->string.unicode();
|
---|
| 262 | int current = 0;
|
---|
| 263 |
|
---|
| 264 | QChar::Direction dir = QChar::DirON;
|
---|
| 265 | BidiStatus status;
|
---|
| 266 | QChar::Direction sdir = direction( *unicode );
|
---|
| 267 | if ( sdir != QChar::DirL && sdir != QChar::DirR && sdir != QChar::DirEN && sdir != QChar::DirAN )
|
---|
| 268 | sdir = QChar::DirON;
|
---|
| 269 | status.eor = sdir;
|
---|
| 270 | status.lastStrong = rightToLeft ? QChar::DirR : QChar::DirL;;
|
---|
| 271 | status.last = status.lastStrong;
|
---|
| 272 | status.dir = sdir;
|
---|
| 273 |
|
---|
| 274 |
|
---|
| 275 |
|
---|
| 276 | while ( current <= length ) {
|
---|
| 277 |
|
---|
| 278 | QChar::Direction dirCurrent;
|
---|
| 279 | if ( current == (int)length )
|
---|
| 280 | dirCurrent = control.basicDirection();
|
---|
| 281 | else
|
---|
| 282 | dirCurrent = direction( unicode[current] );
|
---|
| 283 |
|
---|
| 284 | #if (BIDI_DEBUG >= 2)
|
---|
| 285 | cout << "pos=" << current << " dir=" << directions[dir]
|
---|
| 286 | << " current=" << directions[dirCurrent] << " last=" << directions[status.last]
|
---|
| 287 | << " eor=" << eor << "/" << directions[status.eor]
|
---|
| 288 | << " sor=" << sor << " lastStrong="
|
---|
| 289 | << directions[status.lastStrong]
|
---|
| 290 | << " level=" << (int)control.level() << endl;
|
---|
| 291 | #endif
|
---|
| 292 |
|
---|
| 293 | switch(dirCurrent) {
|
---|
| 294 |
|
---|
| 295 | // embedding and overrides (X1-X9 in the BiDi specs)
|
---|
| 296 | case QChar::DirRLE:
|
---|
| 297 | case QChar::DirRLO:
|
---|
| 298 | case QChar::DirLRE:
|
---|
| 299 | case QChar::DirLRO:
|
---|
| 300 | {
|
---|
| 301 | bool rtl = (dirCurrent == QChar::DirRLE || dirCurrent == QChar::DirRLO );
|
---|
| 302 | bool override = (dirCurrent == QChar::DirLRO || dirCurrent == QChar::DirRLO );
|
---|
| 303 |
|
---|
| 304 | uchar level = control.level();
|
---|
| 305 | if( (level%2 != 0) == rtl )
|
---|
| 306 | level += 2;
|
---|
| 307 | else
|
---|
| 308 | level++;
|
---|
| 309 | if(level < 61) {
|
---|
| 310 | eor = current-1;
|
---|
| 311 | appendItems(engine, sor, eor, control, dir);
|
---|
| 312 | eor = current;
|
---|
| 313 | control.embed( level, override );
|
---|
| 314 | QChar::Direction edir = (rtl ? QChar::DirR : QChar::DirL );
|
---|
| 315 | dir = status.eor = edir;
|
---|
| 316 | status.lastStrong = edir;
|
---|
| 317 | }
|
---|
| 318 | break;
|
---|
| 319 | }
|
---|
| 320 | case QChar::DirPDF:
|
---|
| 321 | {
|
---|
| 322 | if (dir != control.direction()) {
|
---|
| 323 | eor = current-1;
|
---|
| 324 | appendItems(engine, sor, eor, control, dir);
|
---|
| 325 | dir = control.direction();
|
---|
| 326 | }
|
---|
| 327 | eor = current;
|
---|
| 328 | appendItems(engine, sor, eor, control, dir);
|
---|
| 329 | dir = QChar::DirON; status.eor = QChar::DirON;
|
---|
| 330 | status.last = control.direction();
|
---|
| 331 | control.pdf();
|
---|
| 332 | if ( control.override() )
|
---|
| 333 | dir = control.direction();
|
---|
| 334 | else
|
---|
| 335 | dir = QChar::DirON;
|
---|
| 336 | status.lastStrong = control.direction();
|
---|
| 337 | break;
|
---|
| 338 | }
|
---|
| 339 |
|
---|
| 340 | // strong types
|
---|
| 341 | case QChar::DirL:
|
---|
| 342 | if(dir == QChar::DirON)
|
---|
| 343 | dir = QChar::DirL;
|
---|
| 344 | switch(status.last)
|
---|
| 345 | {
|
---|
| 346 | case QChar::DirL:
|
---|
| 347 | eor = current; status.eor = QChar::DirL; break;
|
---|
| 348 | case QChar::DirR:
|
---|
| 349 | case QChar::DirAL:
|
---|
| 350 | case QChar::DirEN:
|
---|
| 351 | case QChar::DirAN:
|
---|
| 352 | if ( !first ) {
|
---|
| 353 | appendItems(engine, sor, eor, control, dir);
|
---|
| 354 | dir = eor < length ? direction( unicode[eor] ) : control.basicDirection();
|
---|
| 355 | status.eor = dir;
|
---|
| 356 | }
|
---|
| 357 | break;
|
---|
| 358 | case QChar::DirES:
|
---|
| 359 | case QChar::DirET:
|
---|
| 360 | case QChar::DirCS:
|
---|
| 361 | case QChar::DirBN:
|
---|
| 362 | case QChar::DirB:
|
---|
| 363 | case QChar::DirS:
|
---|
| 364 | case QChar::DirWS:
|
---|
| 365 | case QChar::DirON:
|
---|
| 366 | if(dir != QChar::DirL) {
|
---|
| 367 | //last stuff takes embedding dir
|
---|
| 368 | if( control.direction() == QChar::DirR ) {
|
---|
| 369 | if(status.eor != QChar::DirR) {
|
---|
| 370 | // AN or EN
|
---|
| 371 | appendItems(engine, sor, eor, control, dir);
|
---|
| 372 | status.eor = QChar::DirON;
|
---|
| 373 | dir = QChar::DirR;
|
---|
| 374 | }
|
---|
| 375 | eor = current - 1;
|
---|
| 376 | appendItems(engine, sor, eor, control, dir);
|
---|
| 377 | dir = eor < length ? direction( unicode[eor] ) : control.basicDirection();
|
---|
| 378 | status.eor = dir;
|
---|
| 379 | } else {
|
---|
| 380 | if(status.eor != QChar::DirL) {
|
---|
| 381 | appendItems(engine, sor, eor, control, dir);
|
---|
| 382 | status.eor = QChar::DirON;
|
---|
| 383 | dir = QChar::DirL;
|
---|
| 384 | } else {
|
---|
| 385 | eor = current; status.eor = QChar::DirL; break;
|
---|
| 386 | }
|
---|
| 387 | }
|
---|
| 388 | } else {
|
---|
| 389 | eor = current; status.eor = QChar::DirL;
|
---|
| 390 | }
|
---|
| 391 | default:
|
---|
| 392 | break;
|
---|
| 393 | }
|
---|
| 394 | status.lastStrong = QChar::DirL;
|
---|
| 395 | break;
|
---|
| 396 | case QChar::DirAL:
|
---|
| 397 | case QChar::DirR:
|
---|
| 398 | if(dir == QChar::DirON) dir = QChar::DirR;
|
---|
| 399 | switch(status.last)
|
---|
| 400 | {
|
---|
| 401 | case QChar::DirL:
|
---|
| 402 | case QChar::DirEN:
|
---|
| 403 | case QChar::DirAN:
|
---|
| 404 | if ( !first ) {
|
---|
| 405 | appendItems(engine, sor, eor, control, dir);
|
---|
| 406 | dir = QChar::DirON; status.eor = QChar::DirON;
|
---|
| 407 | break;
|
---|
| 408 | }
|
---|
| 409 | case QChar::DirR:
|
---|
| 410 | case QChar::DirAL:
|
---|
| 411 | eor = current; status.eor = QChar::DirR; break;
|
---|
| 412 | case QChar::DirES:
|
---|
| 413 | case QChar::DirET:
|
---|
| 414 | case QChar::DirCS:
|
---|
| 415 | case QChar::DirBN:
|
---|
| 416 | case QChar::DirB:
|
---|
| 417 | case QChar::DirS:
|
---|
| 418 | case QChar::DirWS:
|
---|
| 419 | case QChar::DirON:
|
---|
| 420 | if( status.eor != QChar::DirR && status.eor != QChar::DirAL ) {
|
---|
| 421 | //last stuff takes embedding dir
|
---|
| 422 | if(control.direction() == QChar::DirR || status.lastStrong == QChar::DirR) {
|
---|
| 423 | appendItems(engine, sor, eor, control, dir);
|
---|
| 424 | dir = QChar::DirON; status.eor = QChar::DirON;
|
---|
| 425 | dir = QChar::DirR;
|
---|
| 426 | eor = current;
|
---|
| 427 | } else {
|
---|
| 428 | eor = current - 1;
|
---|
| 429 | appendItems(engine, sor, eor, control, dir);
|
---|
| 430 | dir = QChar::DirON; status.eor = QChar::DirON;
|
---|
| 431 | dir = QChar::DirR;
|
---|
| 432 | }
|
---|
| 433 | } else {
|
---|
| 434 | eor = current; status.eor = QChar::DirR;
|
---|
| 435 | }
|
---|
| 436 | default:
|
---|
| 437 | break;
|
---|
| 438 | }
|
---|
| 439 | status.lastStrong = dirCurrent;
|
---|
| 440 | break;
|
---|
| 441 |
|
---|
| 442 | // weak types:
|
---|
| 443 |
|
---|
| 444 | case QChar::DirNSM:
|
---|
| 445 | if (eor == current-1)
|
---|
| 446 | eor = current;
|
---|
| 447 | break;
|
---|
| 448 | case QChar::DirEN:
|
---|
| 449 | // if last strong was AL change EN to AN
|
---|
| 450 | if(status.lastStrong != QChar::DirAL) {
|
---|
| 451 | if(dir == QChar::DirON) {
|
---|
| 452 | if(status.lastStrong == QChar::DirL)
|
---|
| 453 | dir = QChar::DirL;
|
---|
| 454 | else
|
---|
| 455 | dir = QChar::DirEN;
|
---|
| 456 | }
|
---|
| 457 | switch(status.last)
|
---|
| 458 | {
|
---|
| 459 | case QChar::DirET:
|
---|
| 460 | if ( status.lastStrong == QChar::DirR || status.lastStrong == QChar::DirAL ) {
|
---|
| 461 | appendItems(engine, sor, eor, control, dir);
|
---|
| 462 | status.eor = QChar::DirON;
|
---|
| 463 | dir = QChar::DirAN;
|
---|
| 464 | }
|
---|
| 465 | // fall through
|
---|
| 466 | case QChar::DirEN:
|
---|
| 467 | case QChar::DirL:
|
---|
| 468 | eor = current;
|
---|
| 469 | status.eor = dirCurrent;
|
---|
| 470 | break;
|
---|
| 471 | case QChar::DirR:
|
---|
| 472 | case QChar::DirAL:
|
---|
| 473 | case QChar::DirAN:
|
---|
| 474 | if ( !first )
|
---|
| 475 | appendItems(engine, sor, eor, control, dir);
|
---|
| 476 | status.eor = QChar::DirEN;
|
---|
| 477 | dir = QChar::DirAN; break;
|
---|
| 478 | case QChar::DirES:
|
---|
| 479 | case QChar::DirCS:
|
---|
| 480 | if(status.eor == QChar::DirEN || dir == QChar::DirAN) {
|
---|
| 481 | eor = current; break;
|
---|
| 482 | }
|
---|
| 483 | case QChar::DirBN:
|
---|
| 484 | case QChar::DirB:
|
---|
| 485 | case QChar::DirS:
|
---|
| 486 | case QChar::DirWS:
|
---|
| 487 | case QChar::DirON:
|
---|
| 488 | if(status.eor == QChar::DirR) {
|
---|
| 489 | // neutrals go to R
|
---|
| 490 | eor = current - 1;
|
---|
| 491 | appendItems(engine, sor, eor, control, dir);
|
---|
| 492 | dir = QChar::DirON; status.eor = QChar::DirEN;
|
---|
| 493 | dir = QChar::DirAN;
|
---|
| 494 | }
|
---|
| 495 | else if( status.eor == QChar::DirL ||
|
---|
| 496 | (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
|
---|
| 497 | eor = current; status.eor = dirCurrent;
|
---|
| 498 | } else {
|
---|
| 499 | // numbers on both sides, neutrals get right to left direction
|
---|
| 500 | if(dir != QChar::DirL) {
|
---|
| 501 | appendItems(engine, sor, eor, control, dir);
|
---|
| 502 | dir = QChar::DirON; status.eor = QChar::DirON;
|
---|
| 503 | eor = current - 1;
|
---|
| 504 | dir = QChar::DirR;
|
---|
| 505 | appendItems(engine, sor, eor, control, dir);
|
---|
| 506 | dir = QChar::DirON; status.eor = QChar::DirON;
|
---|
| 507 | dir = QChar::DirAN;
|
---|
| 508 | } else {
|
---|
| 509 | eor = current; status.eor = dirCurrent;
|
---|
| 510 | }
|
---|
| 511 | }
|
---|
| 512 | default:
|
---|
| 513 | break;
|
---|
| 514 | }
|
---|
| 515 | break;
|
---|
| 516 | }
|
---|
| 517 | case QChar::DirAN:
|
---|
| 518 | dirCurrent = QChar::DirAN;
|
---|
| 519 | if(dir == QChar::DirON) dir = QChar::DirAN;
|
---|
| 520 | switch(status.last)
|
---|
| 521 | {
|
---|
| 522 | case QChar::DirL:
|
---|
| 523 | case QChar::DirAN:
|
---|
| 524 | eor = current; status.eor = QChar::DirAN; break;
|
---|
| 525 | case QChar::DirR:
|
---|
| 526 | case QChar::DirAL:
|
---|
| 527 | case QChar::DirEN:
|
---|
| 528 | if ( !first )
|
---|
| 529 | appendItems(engine, sor, eor, control, dir);
|
---|
| 530 | dir = QChar::DirON; status.eor = QChar::DirAN;
|
---|
| 531 | break;
|
---|
| 532 | case QChar::DirCS:
|
---|
| 533 | if(status.eor == QChar::DirAN) {
|
---|
| 534 | eor = current; break;
|
---|
| 535 | }
|
---|
| 536 | case QChar::DirES:
|
---|
| 537 | case QChar::DirET:
|
---|
| 538 | case QChar::DirBN:
|
---|
| 539 | case QChar::DirB:
|
---|
| 540 | case QChar::DirS:
|
---|
| 541 | case QChar::DirWS:
|
---|
| 542 | case QChar::DirON:
|
---|
| 543 | if(status.eor == QChar::DirR) {
|
---|
| 544 | // neutrals go to R
|
---|
| 545 | eor = current - 1;
|
---|
| 546 | appendItems(engine, sor, eor, control, dir);
|
---|
| 547 | status.eor = QChar::DirAN;
|
---|
| 548 | dir = QChar::DirAN;
|
---|
| 549 | } else if( status.eor == QChar::DirL ||
|
---|
| 550 | (status.eor == QChar::DirEN && status.lastStrong == QChar::DirL)) {
|
---|
| 551 | eor = current; status.eor = dirCurrent;
|
---|
| 552 | } else {
|
---|
| 553 | // numbers on both sides, neutrals get right to left direction
|
---|
| 554 | if(dir != QChar::DirL) {
|
---|
| 555 | appendItems(engine, sor, eor, control, dir);
|
---|
| 556 | status.eor = QChar::DirON;
|
---|
| 557 | eor = current - 1;
|
---|
| 558 | dir = QChar::DirR;
|
---|
| 559 | appendItems(engine, sor, eor, control, dir);
|
---|
| 560 | status.eor = QChar::DirAN;
|
---|
| 561 | dir = QChar::DirAN;
|
---|
| 562 | } else {
|
---|
| 563 | eor = current; status.eor = dirCurrent;
|
---|
| 564 | }
|
---|
| 565 | }
|
---|
| 566 | default:
|
---|
| 567 | break;
|
---|
| 568 | }
|
---|
| 569 | break;
|
---|
| 570 | case QChar::DirES:
|
---|
| 571 | case QChar::DirCS:
|
---|
| 572 | break;
|
---|
| 573 | case QChar::DirET:
|
---|
| 574 | if(status.last == QChar::DirEN) {
|
---|
| 575 | dirCurrent = QChar::DirEN;
|
---|
| 576 | eor = current; status.eor = dirCurrent;
|
---|
| 577 | }
|
---|
| 578 | break;
|
---|
| 579 |
|
---|
| 580 | // boundary neutrals should be ignored
|
---|
| 581 | case QChar::DirBN:
|
---|
| 582 | break;
|
---|
| 583 | // neutrals
|
---|
| 584 | case QChar::DirB:
|
---|
| 585 | // ### what do we do with newline and paragraph separators that come to here?
|
---|
| 586 | break;
|
---|
| 587 | case QChar::DirS:
|
---|
| 588 | // ### implement rule L1
|
---|
| 589 | break;
|
---|
| 590 | case QChar::DirWS:
|
---|
| 591 | case QChar::DirON:
|
---|
| 592 | break;
|
---|
| 593 | default:
|
---|
| 594 | break;
|
---|
| 595 | }
|
---|
| 596 |
|
---|
| 597 | //cout << " after: dir=" << // dir << " current=" << dirCurrent << " last=" << status.last << " eor=" << status.eor << " lastStrong=" << status.lastStrong << " embedding=" << control.direction() << endl;
|
---|
| 598 |
|
---|
| 599 | if(current >= (int)length) break;
|
---|
| 600 |
|
---|
| 601 | // set status.last as needed.
|
---|
| 602 | switch(dirCurrent) {
|
---|
| 603 | case QChar::DirET:
|
---|
| 604 | case QChar::DirES:
|
---|
| 605 | case QChar::DirCS:
|
---|
| 606 | case QChar::DirS:
|
---|
| 607 | case QChar::DirWS:
|
---|
| 608 | case QChar::DirON:
|
---|
| 609 | switch(status.last)
|
---|
| 610 | {
|
---|
| 611 | case QChar::DirL:
|
---|
| 612 | case QChar::DirR:
|
---|
| 613 | case QChar::DirAL:
|
---|
| 614 | case QChar::DirEN:
|
---|
| 615 | case QChar::DirAN:
|
---|
| 616 | status.last = dirCurrent;
|
---|
| 617 | break;
|
---|
| 618 | default:
|
---|
| 619 | status.last = QChar::DirON;
|
---|
| 620 | }
|
---|
| 621 | break;
|
---|
| 622 | case QChar::DirNSM:
|
---|
| 623 | case QChar::DirBN:
|
---|
| 624 | // ignore these
|
---|
| 625 | break;
|
---|
| 626 | case QChar::DirLRO:
|
---|
| 627 | case QChar::DirLRE:
|
---|
| 628 | status.last = QChar::DirL;
|
---|
| 629 | break;
|
---|
| 630 | case QChar::DirRLO:
|
---|
| 631 | case QChar::DirRLE:
|
---|
| 632 | status.last = QChar::DirR;
|
---|
| 633 | break;
|
---|
| 634 | case QChar::DirEN:
|
---|
| 635 | if ( status.last == QChar::DirL ) {
|
---|
| 636 | status.last = QChar::DirL;
|
---|
| 637 | break;
|
---|
| 638 | }
|
---|
| 639 | // fall through
|
---|
| 640 | default:
|
---|
| 641 | status.last = dirCurrent;
|
---|
| 642 | }
|
---|
| 643 |
|
---|
| 644 | first = FALSE;
|
---|
| 645 | ++current;
|
---|
| 646 | }
|
---|
| 647 |
|
---|
| 648 | #if (BIDI_DEBUG >= 1)
|
---|
| 649 | cout << "reached end of line current=" << current << ", eor=" << eor << endl;
|
---|
| 650 | #endif
|
---|
| 651 | eor = current - 1; // remove dummy char
|
---|
| 652 |
|
---|
| 653 | if ( sor <= eor )
|
---|
| 654 | appendItems(engine, sor, eor, control, dir);
|
---|
| 655 |
|
---|
| 656 |
|
---|
| 657 | }
|
---|
| 658 |
|
---|
| 659 | void QTextEngine::bidiReorder( int numItems, const Q_UINT8 *levels, int *visualOrder )
|
---|
| 660 | {
|
---|
| 661 |
|
---|
| 662 | // first find highest and lowest levels
|
---|
| 663 | uchar levelLow = 128;
|
---|
| 664 | uchar levelHigh = 0;
|
---|
| 665 | int i = 0;
|
---|
| 666 | while ( i < numItems ) {
|
---|
| 667 | //printf("level = %d\n", r->level);
|
---|
| 668 | if ( levels[i] > levelHigh )
|
---|
| 669 | levelHigh = levels[i];
|
---|
| 670 | if ( levels[i] < levelLow )
|
---|
| 671 | levelLow = levels[i];
|
---|
| 672 | i++;
|
---|
| 673 | }
|
---|
| 674 |
|
---|
| 675 | // implements reordering of the line (L2 according to BiDi spec):
|
---|
| 676 | // L2. From the highest level found in the text to the lowest odd level on each line,
|
---|
| 677 | // reverse any contiguous sequence of characters that are at that level or higher.
|
---|
| 678 |
|
---|
| 679 | // reversing is only done up to the lowest odd level
|
---|
| 680 | if(!(levelLow%2)) levelLow++;
|
---|
| 681 |
|
---|
| 682 | #if (BIDI_DEBUG >= 1)
|
---|
| 683 | cout << "reorderLine: lineLow = " << (uint)levelLow << ", lineHigh = " << (uint)levelHigh << endl;
|
---|
| 684 | #endif
|
---|
| 685 |
|
---|
| 686 | int count = numItems - 1;
|
---|
| 687 | for ( i = 0; i < numItems; i++ )
|
---|
| 688 | visualOrder[i] = i;
|
---|
| 689 |
|
---|
| 690 | while(levelHigh >= levelLow) {
|
---|
| 691 | int i = 0;
|
---|
| 692 | while ( i < count ) {
|
---|
| 693 | while(i < count && levels[i] < levelHigh) i++;
|
---|
| 694 | int start = i;
|
---|
| 695 | while(i <= count && levels[i] >= levelHigh) i++;
|
---|
| 696 | int end = i-1;
|
---|
| 697 |
|
---|
| 698 | if(start != end) {
|
---|
| 699 | //cout << "reversing from " << start << " to " << end << endl;
|
---|
| 700 | for(int j = 0; j < (end-start+1)/2; j++) {
|
---|
| 701 | int tmp = visualOrder[start+j];
|
---|
| 702 | visualOrder[start+j] = visualOrder[end-j];
|
---|
| 703 | visualOrder[end-j] = tmp;
|
---|
| 704 | }
|
---|
| 705 | }
|
---|
| 706 | i++;
|
---|
| 707 | }
|
---|
| 708 | levelHigh--;
|
---|
| 709 | }
|
---|
| 710 |
|
---|
| 711 | #if (BIDI_DEBUG >= 1)
|
---|
| 712 | cout << "visual order is:" << endl;
|
---|
| 713 | for ( i = 0; i < numItems; i++ )
|
---|
| 714 | cout << visualOrder[i] << endl;
|
---|
| 715 | #endif
|
---|
| 716 | }
|
---|
| 717 |
|
---|
| 718 |
|
---|
| 719 | // -----------------------------------------------------------------------------------------------------
|
---|
| 720 | //
|
---|
| 721 | // The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
|
---|
| 722 | //
|
---|
| 723 | // -----------------------------------------------------------------------------------------------------
|
---|
| 724 |
|
---|
| 725 | /* The Unicode algorithm does in our opinion allow line breaks at some
|
---|
| 726 | places they shouldn't be allowed. The following changes were thus
|
---|
| 727 | made in comparison to the Unicode reference:
|
---|
| 728 |
|
---|
| 729 | CL->AL from Dbk to Ibk
|
---|
| 730 | CL->PR from Dbk to Ibk
|
---|
| 731 | EX->AL from Dbk to Ibk
|
---|
| 732 | IS->AL from Dbk to Ibk
|
---|
| 733 | PO->AL from Dbk to Ibk
|
---|
| 734 | SY->AL from Dbk to Ibk
|
---|
| 735 | SY->PO from Dbk to Ibk
|
---|
| 736 | SY->PR from Dbk to Ibk
|
---|
| 737 | SY->OP from Dbk to Ibk
|
---|
| 738 | Al->OP from Dbk to Ibk
|
---|
| 739 | AL->HY from Dbk to Ibk
|
---|
| 740 | AL->PR from Dbk to Ibk
|
---|
| 741 | AL->PO from Dbk to Ibk
|
---|
| 742 | PR->PR from Dbk to Ibk
|
---|
| 743 | PO->PO from Dbk to Ibk
|
---|
| 744 | PR->PO from Dbk to Ibk
|
---|
| 745 | PO->PR from Dbk to Ibk
|
---|
| 746 | HY->PO from Dbk to Ibk
|
---|
| 747 | HY->PR from Dbk to Ibk
|
---|
| 748 | HY->OP from Dbk to Ibk
|
---|
| 749 | PO->OP from Dbk to Ibk
|
---|
| 750 | NU->EX from Dbk to Ibk
|
---|
| 751 | NU->PR from Dbk to Ibk
|
---|
| 752 | PO->NU from Dbk to Ibk
|
---|
| 753 | EX->PO from Dbk to Ibk
|
---|
| 754 | */
|
---|
| 755 |
|
---|
| 756 | enum break_action {
|
---|
| 757 | Dbk, // Direct break
|
---|
| 758 | Ibk, // Indirect break; only allowed if space between the two chars
|
---|
| 759 | Pbk // Prohibited break; no break allowed even if space between chars
|
---|
| 760 | };
|
---|
| 761 |
|
---|
| 762 | // The following line break classes are not treated by the table:
|
---|
| 763 | // SA, BK, CR, LF, SG, CB, SP
|
---|
| 764 | static const Q_UINT8 breakTable[QUnicodeTables::LineBreak_CM+1][QUnicodeTables::LineBreak_CM+1] =
|
---|
| 765 | {
|
---|
| 766 | // OP, CL, QU, GL, NS, EX, SY, IS, PR, PO, NU, AL, ID, IN, HY, BA, BB, B2, ZW, CM
|
---|
| 767 | { Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk, Pbk }, // OP
|
---|
| 768 | { Dbk, Pbk, Ibk, Pbk, Pbk, Pbk, Pbk, Pbk, Ibk, Ibk, Dbk, Ibk, Dbk, Dbk, Ibk, Ibk, Pbk, Pbk, Pbk, Pbk }, // CL
|
---|
| 769 | { Pbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Pbk, Pbk }, // QU
|
---|
| 770 | { Ibk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Pbk, Pbk }, // GL
|
---|
| 771 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // NS
|
---|
| 772 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Ibk, Ibk, Ibk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // EX
|
---|
| 773 | { Ibk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // SY
|
---|
| 774 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // IS
|
---|
| 775 | { Ibk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Ibk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Pbk }, // PR
|
---|
| 776 | { Ibk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // PO
|
---|
| 777 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Dbk, Ibk, Ibk, Ibk, Dbk, Dbk, Pbk, Pbk }, // NU
|
---|
| 778 | { Ibk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Dbk, Ibk, Ibk, Ibk, Dbk, Dbk, Pbk, Pbk }, // AL
|
---|
| 779 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Ibk, Dbk, Dbk, Dbk, Ibk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // ID
|
---|
| 780 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Dbk, Dbk, Dbk, Dbk, Ibk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // IN
|
---|
| 781 | { Ibk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // HY
|
---|
| 782 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Ibk, Ibk, Dbk, Dbk, Pbk, Ibk }, // BA
|
---|
| 783 | { Ibk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Ibk, Pbk, Ibk }, // BB
|
---|
| 784 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Ibk, Ibk, Dbk, Pbk, Pbk, Ibk }, // B2
|
---|
| 785 | { Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Dbk, Pbk, Ibk }, // ZW
|
---|
| 786 | { Dbk, Pbk, Ibk, Pbk, Ibk, Pbk, Pbk, Pbk, Dbk, Ibk, Dbk, Dbk, Dbk, Ibk, Ibk, Ibk, Dbk, Dbk, Pbk, Pbk } // CM
|
---|
| 787 | };
|
---|
| 788 |
|
---|
| 789 | // set the soft break flag at every possible line breaking point. This needs correct clustering information.
|
---|
| 790 | static void calcLineBreaks(const QString &str, QCharAttributes *charAttributes)
|
---|
| 791 | {
|
---|
| 792 | int len = str.length();
|
---|
| 793 | if (!len)
|
---|
| 794 | return;
|
---|
| 795 |
|
---|
| 796 | const QChar *uc = str.unicode();
|
---|
| 797 | int cls = lineBreakClass(*uc);
|
---|
| 798 | if (cls == QUnicodeTables::LineBreak_CM)
|
---|
| 799 | cls = QUnicodeTables::LineBreak_ID;
|
---|
| 800 |
|
---|
| 801 | charAttributes[0].softBreak = FALSE;
|
---|
| 802 |
|
---|
| 803 | for (int i = 1; i < len; ++i) {
|
---|
| 804 | int ncls = lineBreakClass(uc[i]);
|
---|
| 805 |
|
---|
| 806 | if (ncls == QUnicodeTables::LineBreak_SP || ncls == QUnicodeTables::LineBreak_CM) {
|
---|
| 807 | charAttributes[i].softBreak = FALSE;
|
---|
| 808 | continue;
|
---|
| 809 | }
|
---|
| 810 | if (ncls >= QUnicodeTables::LineBreak_SA) {
|
---|
| 811 | // ### handle complex case
|
---|
| 812 | ncls = QUnicodeTables::LineBreak_ID;
|
---|
| 813 | }
|
---|
| 814 | int brk = charAttributes[i].charStop ? breakTable[cls][ncls] : (int)Pbk;
|
---|
| 815 | if (brk == Ibk)
|
---|
| 816 | charAttributes[i].softBreak = (lineBreakClass(uc[i-1]) == QUnicodeTables::LineBreak_SP);
|
---|
| 817 | else
|
---|
| 818 | charAttributes[i].softBreak = (brk == Dbk);
|
---|
| 819 | // qDebug("char = %c %04x, cls=%d, ncls=%d, brk=%d soft=%d", uc[i].cell(), uc[i].unicode(), cls, ncls, brk, charAttributes[i].softBreak);
|
---|
| 820 | cls = ncls;
|
---|
| 821 | }
|
---|
| 822 | }
|
---|
| 823 |
|
---|
| 824 | #if defined( Q_WS_X11 ) || defined ( Q_WS_QWS )
|
---|
| 825 | # include "qtextengine_unix.cpp"
|
---|
| 826 | #elif defined( Q_WS_WIN )
|
---|
| 827 | # include "qtextengine_win.cpp"
|
---|
[8] | 828 | #elif defined( Q_WS_PM )
|
---|
| 829 | # include "qtextengine_pm.cpp"
|
---|
[2] | 830 | #elif defined( Q_WS_MAC )
|
---|
| 831 | # include "qtextengine_mac.cpp"
|
---|
| 832 | #endif
|
---|
| 833 |
|
---|
| 834 |
|
---|
| 835 |
|
---|
| 836 | QTextEngine::QTextEngine( const QString &str, QFontPrivate *f )
|
---|
| 837 | : string( str ), fnt( f ), direction( QChar::DirON ), haveCharAttributes( FALSE ), widthOnly( FALSE )
|
---|
| 838 | {
|
---|
| 839 | #ifdef Q_WS_WIN
|
---|
| 840 | if ( !resolvedUsp10 )
|
---|
| 841 | resolveUsp10();
|
---|
| 842 | #endif
|
---|
| 843 | if ( fnt ) fnt->ref();
|
---|
| 844 |
|
---|
| 845 | num_glyphs = QMAX( 16, str.length()*3/2 );
|
---|
| 846 | int space_charAttributes = (sizeof(QCharAttributes)*str.length()+sizeof(void*)-1)/sizeof(void*);
|
---|
| 847 | int space_logClusters = (sizeof(unsigned short)*str.length()+sizeof(void*)-1)/sizeof(void*);
|
---|
| 848 | int space_glyphs = (sizeof(glyph_t)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 849 | int space_advances = (sizeof(advance_t)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 850 | int space_offsets = (sizeof(qoffset_t)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 851 | int space_glyphAttributes = (sizeof(GlyphAttributes)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 852 |
|
---|
| 853 | allocated = space_charAttributes + space_glyphs + space_advances +
|
---|
| 854 | space_offsets + space_logClusters + space_glyphAttributes;
|
---|
| 855 | memory = (void **)::malloc( allocated*sizeof( void * ) );
|
---|
| 856 | memset( memory, 0, allocated*sizeof( void * ) );
|
---|
| 857 |
|
---|
| 858 | void **m = memory;
|
---|
| 859 | m += space_charAttributes;
|
---|
| 860 | logClustersPtr = (unsigned short *) m;
|
---|
| 861 | m += space_logClusters;
|
---|
| 862 | glyphPtr = (glyph_t *) m;
|
---|
| 863 | m += space_glyphs;
|
---|
| 864 | advancePtr = (advance_t *) m;
|
---|
| 865 | m += space_advances;
|
---|
| 866 | offsetsPtr = (qoffset_t *) m;
|
---|
| 867 | m += space_offsets;
|
---|
| 868 | glyphAttributesPtr = (GlyphAttributes *) m;
|
---|
| 869 |
|
---|
| 870 | used = 0;
|
---|
| 871 | }
|
---|
| 872 |
|
---|
| 873 | QTextEngine::~QTextEngine()
|
---|
| 874 | {
|
---|
| 875 | if ( fnt && fnt->deref())
|
---|
| 876 | delete fnt;
|
---|
| 877 | free( memory );
|
---|
| 878 | allocated = 0;
|
---|
| 879 | }
|
---|
| 880 |
|
---|
| 881 | void QTextEngine::reallocate( int totalGlyphs )
|
---|
| 882 | {
|
---|
| 883 | int new_num_glyphs = totalGlyphs;
|
---|
| 884 | int space_charAttributes = (sizeof(QCharAttributes)*string.length()+sizeof(void*)-1)/sizeof(void*);
|
---|
| 885 | int space_logClusters = (sizeof(unsigned short)*string.length()+sizeof(void*)-1)/sizeof(void*);
|
---|
| 886 | int space_glyphs = (sizeof(glyph_t)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 887 | int space_advances = (sizeof(advance_t)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 888 | int space_offsets = (sizeof(qoffset_t)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 889 | int space_glyphAttributes = (sizeof(GlyphAttributes)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
|
---|
| 890 |
|
---|
| 891 | int newAllocated = space_charAttributes + space_glyphs + space_advances +
|
---|
| 892 | space_offsets + space_logClusters + space_glyphAttributes;
|
---|
| 893 | void ** newMemory = (void **)::malloc( newAllocated*sizeof( void * ) );
|
---|
| 894 |
|
---|
| 895 | void **nm = newMemory;
|
---|
| 896 | memcpy( nm, memory, string.length()*sizeof(QCharAttributes) );
|
---|
| 897 | nm += space_charAttributes;
|
---|
| 898 | memcpy( nm, logClustersPtr, num_glyphs*sizeof(unsigned short) );
|
---|
| 899 | logClustersPtr = (unsigned short *) nm;
|
---|
| 900 | nm += space_logClusters;
|
---|
| 901 | memcpy( nm, glyphPtr, num_glyphs*sizeof(glyph_t) );
|
---|
| 902 | glyphPtr = (glyph_t *) nm;
|
---|
| 903 | nm += space_glyphs;
|
---|
| 904 | memcpy( nm, advancePtr, num_glyphs*sizeof(advance_t) );
|
---|
| 905 | advancePtr = (advance_t *) nm;
|
---|
| 906 | nm += space_advances;
|
---|
| 907 | memcpy( nm, offsetsPtr, num_glyphs*sizeof(qoffset_t) );
|
---|
| 908 | offsetsPtr = (qoffset_t *) nm;
|
---|
| 909 | nm += space_offsets;
|
---|
| 910 | memcpy( nm, glyphAttributesPtr, num_glyphs*sizeof(GlyphAttributes) );
|
---|
| 911 | glyphAttributesPtr = (GlyphAttributes *) nm;
|
---|
| 912 |
|
---|
| 913 | free( memory );
|
---|
| 914 | memory = newMemory;
|
---|
| 915 | allocated = newAllocated;
|
---|
| 916 | num_glyphs = new_num_glyphs;
|
---|
| 917 | }
|
---|
| 918 |
|
---|
| 919 | const QCharAttributes *QTextEngine::attributes()
|
---|
| 920 | {
|
---|
| 921 | QCharAttributes *charAttributes = (QCharAttributes *) memory;
|
---|
| 922 | if ( haveCharAttributes )
|
---|
| 923 | return charAttributes;
|
---|
| 924 |
|
---|
| 925 | if ( !items.d )
|
---|
| 926 | itemize();
|
---|
| 927 |
|
---|
| 928 | for ( int i = 0; i < items.size(); i++ ) {
|
---|
| 929 | QScriptItem &si = items[i];
|
---|
| 930 | int from = si.position;
|
---|
| 931 | int len = length( i );
|
---|
| 932 | int script = si.analysis.script;
|
---|
| 933 | Q_ASSERT( script < QFont::NScripts );
|
---|
| 934 | scriptEngines[si.analysis.script].charAttributes( script, string, from, len, charAttributes );
|
---|
| 935 | }
|
---|
| 936 |
|
---|
| 937 | calcLineBreaks(string, charAttributes);
|
---|
| 938 | haveCharAttributes = TRUE;
|
---|
| 939 | return charAttributes;
|
---|
| 940 | }
|
---|
| 941 |
|
---|
| 942 | void QTextEngine::splitItem( int item, int pos )
|
---|
| 943 | {
|
---|
| 944 | if ( pos <= 0 )
|
---|
| 945 | return;
|
---|
| 946 |
|
---|
| 947 | // we have to ensure we get correct shaping for arabic and other
|
---|
| 948 | // complex languages so we have to call shape _before_ we split the item.
|
---|
| 949 | shape(item);
|
---|
| 950 |
|
---|
| 951 | if ( items.d->size == items.d->alloc )
|
---|
| 952 | items.resize( items.d->size + 1 );
|
---|
| 953 |
|
---|
| 954 | int numMove = items.d->size - item-1;
|
---|
| 955 | if ( numMove > 0 )
|
---|
| 956 | memmove( items.d->items + item+2, items.d->items +item+1, numMove*sizeof( QScriptItem ) );
|
---|
| 957 | items.d->size++;
|
---|
| 958 | QScriptItem &newItem = items.d->items[item+1];
|
---|
| 959 | QScriptItem &oldItem = items.d->items[item];
|
---|
| 960 | newItem = oldItem;
|
---|
| 961 | items.d->items[item+1].position += pos;
|
---|
| 962 | if ( newItem.fontEngine )
|
---|
| 963 | newItem.fontEngine->ref();
|
---|
| 964 |
|
---|
| 965 | if (oldItem.num_glyphs) {
|
---|
| 966 | // already shaped, break glyphs aswell
|
---|
| 967 | int breakGlyph = logClusters(&oldItem)[pos];
|
---|
| 968 |
|
---|
| 969 | newItem.num_glyphs = oldItem.num_glyphs - breakGlyph;
|
---|
| 970 | oldItem.num_glyphs = breakGlyph;
|
---|
| 971 | newItem.glyph_data_offset = oldItem.glyph_data_offset + breakGlyph;
|
---|
| 972 |
|
---|
| 973 | for (int i = 0; i < newItem.num_glyphs; i++)
|
---|
| 974 | logClusters(&newItem)[i] -= breakGlyph;
|
---|
| 975 |
|
---|
| 976 | int w = 0;
|
---|
| 977 | const advance_t *a = advances(&oldItem);
|
---|
| 978 | for(int j = 0; j < breakGlyph; ++j)
|
---|
| 979 | w += *(a++);
|
---|
| 980 |
|
---|
| 981 | newItem.width = oldItem.width - w;
|
---|
| 982 | oldItem.width = w;
|
---|
| 983 | }
|
---|
| 984 |
|
---|
| 985 | // qDebug("split at position %d itempos=%d", pos, item );
|
---|
| 986 | }
|
---|
| 987 |
|
---|
| 988 |
|
---|
| 989 | int QTextEngine::width( int from, int len ) const
|
---|
| 990 | {
|
---|
| 991 | int w = 0;
|
---|
| 992 |
|
---|
| 993 | // qDebug("QTextEngine::width( from = %d, len = %d ), numItems=%d, strleng=%d", from, len, items.size(), string.length() );
|
---|
| 994 | for ( int i = 0; i < items.size(); i++ ) {
|
---|
| 995 | QScriptItem *si = &items[i];
|
---|
| 996 | int pos = si->position;
|
---|
| 997 | int ilen = length( i );
|
---|
| 998 | // qDebug("item %d: from %d len %d", i, pos, ilen );
|
---|
| 999 | if ( pos >= from + len )
|
---|
| 1000 | break;
|
---|
| 1001 | if ( pos + ilen > from ) {
|
---|
| 1002 | if ( !si->num_glyphs )
|
---|
| 1003 | shape( i );
|
---|
| 1004 |
|
---|
| 1005 | advance_t *advances = this->advances( si );
|
---|
| 1006 | unsigned short *logClusters = this->logClusters( si );
|
---|
| 1007 |
|
---|
| 1008 | // fprintf( stderr, " logclusters:" );
|
---|
| 1009 | // for ( int k = 0; k < ilen; k++ )
|
---|
| 1010 | // fprintf( stderr, " %d", logClusters[k] );
|
---|
| 1011 | // fprintf( stderr, "\n" );
|
---|
| 1012 | // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
|
---|
| 1013 | int charFrom = from - pos;
|
---|
| 1014 | if ( charFrom < 0 )
|
---|
| 1015 | charFrom = 0;
|
---|
| 1016 | int glyphStart = logClusters[charFrom];
|
---|
| 1017 | if ( charFrom > 0 && logClusters[charFrom-1] == glyphStart )
|
---|
| 1018 | while ( charFrom < ilen && logClusters[charFrom] == glyphStart )
|
---|
| 1019 | charFrom++;
|
---|
| 1020 | if ( charFrom < ilen ) {
|
---|
| 1021 | glyphStart = logClusters[charFrom];
|
---|
| 1022 | int charEnd = from + len - 1 - pos;
|
---|
| 1023 | if ( charEnd >= ilen )
|
---|
| 1024 | charEnd = ilen-1;
|
---|
| 1025 | int glyphEnd = logClusters[charEnd];
|
---|
| 1026 | while ( charEnd < ilen && logClusters[charEnd] == glyphEnd )
|
---|
| 1027 | charEnd++;
|
---|
| 1028 | glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
|
---|
| 1029 |
|
---|
| 1030 | // qDebug("char: start=%d end=%d / glyph: start = %d, end = %d", charFrom, charEnd, glyphStart, glyphEnd );
|
---|
| 1031 | for ( int i = glyphStart; i < glyphEnd; i++ )
|
---|
| 1032 | w += advances[i];
|
---|
| 1033 | }
|
---|
| 1034 | }
|
---|
| 1035 | }
|
---|
| 1036 | // qDebug(" --> w= %d ", w );
|
---|
| 1037 | return w;
|
---|
| 1038 | }
|
---|
| 1039 |
|
---|
| 1040 | void QTextEngine::itemize( int mode )
|
---|
| 1041 | {
|
---|
| 1042 | if ( !items.d ) {
|
---|
| 1043 | int size = 8;
|
---|
| 1044 | items.d = (QScriptItemArrayPrivate *)malloc( sizeof( QScriptItemArrayPrivate ) +
|
---|
| 1045 | sizeof( QScriptItem ) * size );
|
---|
| 1046 | items.d->alloc = size;
|
---|
| 1047 | }
|
---|
| 1048 | items.d->size = 0;
|
---|
| 1049 | if ( string.length() == 0 )
|
---|
| 1050 | return;
|
---|
| 1051 |
|
---|
| 1052 | if ( !(mode & NoBidi) ) {
|
---|
| 1053 | if ( direction == QChar::DirON )
|
---|
| 1054 | direction = basicDirection( string );
|
---|
| 1055 | bidiItemize( this, direction == QChar::DirR, mode );
|
---|
| 1056 | } else {
|
---|
| 1057 | BidiControl control( FALSE );
|
---|
| 1058 | if ( mode & QTextEngine::SingleLine )
|
---|
| 1059 | control.singleLine = TRUE;
|
---|
| 1060 | int start = 0;
|
---|
| 1061 | int stop = string.length() - 1;
|
---|
| 1062 | appendItems(this, start, stop, control, QChar::DirL);
|
---|
| 1063 | }
|
---|
| 1064 | if ( (mode & WidthOnly) == WidthOnly )
|
---|
| 1065 | widthOnly = TRUE;
|
---|
| 1066 | }
|
---|
| 1067 |
|
---|
| 1068 | glyph_metrics_t QTextEngine::boundingBox( int from, int len ) const
|
---|
| 1069 | {
|
---|
| 1070 | glyph_metrics_t gm;
|
---|
| 1071 |
|
---|
| 1072 | for ( int i = 0; i < items.size(); i++ ) {
|
---|
| 1073 | QScriptItem *si = &items[i];
|
---|
| 1074 | int pos = si->position;
|
---|
| 1075 | int ilen = length( i );
|
---|
| 1076 | if ( pos > from + len )
|
---|
| 1077 | break;
|
---|
| 1078 | if ( pos + len > from ) {
|
---|
| 1079 | if ( !si->num_glyphs )
|
---|
| 1080 | shape( i );
|
---|
| 1081 | advance_t *advances = this->advances( si );
|
---|
| 1082 | unsigned short *logClusters = this->logClusters( si );
|
---|
| 1083 | glyph_t *glyphs = this->glyphs( si );
|
---|
| 1084 | qoffset_t *offsets = this->offsets( si );
|
---|
| 1085 |
|
---|
| 1086 | // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
|
---|
| 1087 | int charFrom = from - pos;
|
---|
| 1088 | if ( charFrom < 0 )
|
---|
| 1089 | charFrom = 0;
|
---|
| 1090 | int glyphStart = logClusters[charFrom];
|
---|
| 1091 | if ( charFrom > 0 && logClusters[charFrom-1] == glyphStart )
|
---|
| 1092 | while ( charFrom < ilen && logClusters[charFrom] == glyphStart )
|
---|
| 1093 | charFrom++;
|
---|
| 1094 | if ( charFrom < ilen ) {
|
---|
| 1095 | glyphStart = logClusters[charFrom];
|
---|
| 1096 | int charEnd = from + len - 1 - pos;
|
---|
| 1097 | if ( charEnd >= ilen )
|
---|
| 1098 | charEnd = ilen-1;
|
---|
| 1099 | int glyphEnd = logClusters[charEnd];
|
---|
| 1100 | while ( charEnd < ilen && logClusters[charEnd] == glyphEnd )
|
---|
| 1101 | charEnd++;
|
---|
| 1102 | glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
|
---|
| 1103 | if ( glyphStart <= glyphEnd ) {
|
---|
| 1104 | QFontEngine *fe = si->fontEngine;
|
---|
| 1105 | glyph_metrics_t m = fe->boundingBox( glyphs+glyphStart, advances+glyphStart,
|
---|
| 1106 | offsets+glyphStart, glyphEnd-glyphStart );
|
---|
| 1107 | gm.x = QMIN( gm.x, m.x + gm.xoff );
|
---|
| 1108 | gm.y = QMIN( gm.y, m.y + gm.yoff );
|
---|
| 1109 | gm.width = QMAX( gm.width, m.width+gm.xoff );
|
---|
| 1110 | gm.height = QMAX( gm.height, m.height+gm.yoff );
|
---|
| 1111 | gm.xoff += m.xoff;
|
---|
| 1112 | gm.yoff += m.yoff;
|
---|
| 1113 | }
|
---|
| 1114 | }
|
---|
| 1115 | }
|
---|
| 1116 | }
|
---|
| 1117 | return gm;
|
---|
| 1118 | }
|
---|