source: vendor/trolltech/current/src/kernel/qtextengine.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 34.4 KB
Line 
1/****************************************************************************
2** $Id: qtextengine.cpp 2 2005-11-16 15:49:26Z 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
57static 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
64struct 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
77struct 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
122static 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
149static 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
240typedef void (* fAppendItems)(QTextEngine *, int &start, int &stop, BidiControl &control, QChar::Direction dir);
241static fAppendItems appendItems = qAppendItems;
242
243// creates the next QScript items.
244static 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
659void 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
756enum 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
764static 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.
790static 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"
828#elif defined( Q_WS_MAC )
829# include "qtextengine_mac.cpp"
830#endif
831
832
833
834QTextEngine::QTextEngine( const QString &str, QFontPrivate *f )
835 : string( str ), fnt( f ), direction( QChar::DirON ), haveCharAttributes( FALSE ), widthOnly( FALSE )
836{
837#ifdef Q_WS_WIN
838 if ( !resolvedUsp10 )
839 resolveUsp10();
840#endif
841 if ( fnt ) fnt->ref();
842
843 num_glyphs = QMAX( 16, str.length()*3/2 );
844 int space_charAttributes = (sizeof(QCharAttributes)*str.length()+sizeof(void*)-1)/sizeof(void*);
845 int space_logClusters = (sizeof(unsigned short)*str.length()+sizeof(void*)-1)/sizeof(void*);
846 int space_glyphs = (sizeof(glyph_t)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
847 int space_advances = (sizeof(advance_t)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
848 int space_offsets = (sizeof(qoffset_t)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
849 int space_glyphAttributes = (sizeof(GlyphAttributes)*num_glyphs+sizeof(void*)-1)/sizeof(void*);
850
851 allocated = space_charAttributes + space_glyphs + space_advances +
852 space_offsets + space_logClusters + space_glyphAttributes;
853 memory = (void **)::malloc( allocated*sizeof( void * ) );
854 memset( memory, 0, allocated*sizeof( void * ) );
855
856 void **m = memory;
857 m += space_charAttributes;
858 logClustersPtr = (unsigned short *) m;
859 m += space_logClusters;
860 glyphPtr = (glyph_t *) m;
861 m += space_glyphs;
862 advancePtr = (advance_t *) m;
863 m += space_advances;
864 offsetsPtr = (qoffset_t *) m;
865 m += space_offsets;
866 glyphAttributesPtr = (GlyphAttributes *) m;
867
868 used = 0;
869}
870
871QTextEngine::~QTextEngine()
872{
873 if ( fnt && fnt->deref())
874 delete fnt;
875 free( memory );
876 allocated = 0;
877}
878
879void QTextEngine::reallocate( int totalGlyphs )
880{
881 int new_num_glyphs = totalGlyphs;
882 int space_charAttributes = (sizeof(QCharAttributes)*string.length()+sizeof(void*)-1)/sizeof(void*);
883 int space_logClusters = (sizeof(unsigned short)*string.length()+sizeof(void*)-1)/sizeof(void*);
884 int space_glyphs = (sizeof(glyph_t)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
885 int space_advances = (sizeof(advance_t)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
886 int space_offsets = (sizeof(qoffset_t)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
887 int space_glyphAttributes = (sizeof(GlyphAttributes)*new_num_glyphs+sizeof(void*)-1)/sizeof(void*);
888
889 int newAllocated = space_charAttributes + space_glyphs + space_advances +
890 space_offsets + space_logClusters + space_glyphAttributes;
891 void ** newMemory = (void **)::malloc( newAllocated*sizeof( void * ) );
892
893 void **nm = newMemory;
894 memcpy( nm, memory, string.length()*sizeof(QCharAttributes) );
895 nm += space_charAttributes;
896 memcpy( nm, logClustersPtr, num_glyphs*sizeof(unsigned short) );
897 logClustersPtr = (unsigned short *) nm;
898 nm += space_logClusters;
899 memcpy( nm, glyphPtr, num_glyphs*sizeof(glyph_t) );
900 glyphPtr = (glyph_t *) nm;
901 nm += space_glyphs;
902 memcpy( nm, advancePtr, num_glyphs*sizeof(advance_t) );
903 advancePtr = (advance_t *) nm;
904 nm += space_advances;
905 memcpy( nm, offsetsPtr, num_glyphs*sizeof(qoffset_t) );
906 offsetsPtr = (qoffset_t *) nm;
907 nm += space_offsets;
908 memcpy( nm, glyphAttributesPtr, num_glyphs*sizeof(GlyphAttributes) );
909 glyphAttributesPtr = (GlyphAttributes *) nm;
910
911 free( memory );
912 memory = newMemory;
913 allocated = newAllocated;
914 num_glyphs = new_num_glyphs;
915}
916
917const QCharAttributes *QTextEngine::attributes()
918{
919 QCharAttributes *charAttributes = (QCharAttributes *) memory;
920 if ( haveCharAttributes )
921 return charAttributes;
922
923 if ( !items.d )
924 itemize();
925
926 for ( int i = 0; i < items.size(); i++ ) {
927 QScriptItem &si = items[i];
928 int from = si.position;
929 int len = length( i );
930 int script = si.analysis.script;
931 Q_ASSERT( script < QFont::NScripts );
932 scriptEngines[si.analysis.script].charAttributes( script, string, from, len, charAttributes );
933 }
934
935 calcLineBreaks(string, charAttributes);
936 haveCharAttributes = TRUE;
937 return charAttributes;
938}
939
940void QTextEngine::splitItem( int item, int pos )
941{
942 if ( pos <= 0 )
943 return;
944
945 // we have to ensure we get correct shaping for arabic and other
946 // complex languages so we have to call shape _before_ we split the item.
947 shape(item);
948
949 if ( items.d->size == items.d->alloc )
950 items.resize( items.d->size + 1 );
951
952 int numMove = items.d->size - item-1;
953 if ( numMove > 0 )
954 memmove( items.d->items + item+2, items.d->items +item+1, numMove*sizeof( QScriptItem ) );
955 items.d->size++;
956 QScriptItem &newItem = items.d->items[item+1];
957 QScriptItem &oldItem = items.d->items[item];
958 newItem = oldItem;
959 items.d->items[item+1].position += pos;
960 if ( newItem.fontEngine )
961 newItem.fontEngine->ref();
962
963 if (oldItem.num_glyphs) {
964 // already shaped, break glyphs aswell
965 int breakGlyph = logClusters(&oldItem)[pos];
966
967 newItem.num_glyphs = oldItem.num_glyphs - breakGlyph;
968 oldItem.num_glyphs = breakGlyph;
969 newItem.glyph_data_offset = oldItem.glyph_data_offset + breakGlyph;
970
971 for (int i = 0; i < newItem.num_glyphs; i++)
972 logClusters(&newItem)[i] -= breakGlyph;
973
974 int w = 0;
975 const advance_t *a = advances(&oldItem);
976 for(int j = 0; j < breakGlyph; ++j)
977 w += *(a++);
978
979 newItem.width = oldItem.width - w;
980 oldItem.width = w;
981 }
982
983// qDebug("split at position %d itempos=%d", pos, item );
984}
985
986
987int QTextEngine::width( int from, int len ) const
988{
989 int w = 0;
990
991// qDebug("QTextEngine::width( from = %d, len = %d ), numItems=%d, strleng=%d", from, len, items.size(), string.length() );
992 for ( int i = 0; i < items.size(); i++ ) {
993 QScriptItem *si = &items[i];
994 int pos = si->position;
995 int ilen = length( i );
996// qDebug("item %d: from %d len %d", i, pos, ilen );
997 if ( pos >= from + len )
998 break;
999 if ( pos + ilen > from ) {
1000 if ( !si->num_glyphs )
1001 shape( i );
1002
1003 advance_t *advances = this->advances( si );
1004 unsigned short *logClusters = this->logClusters( si );
1005
1006// fprintf( stderr, " logclusters:" );
1007// for ( int k = 0; k < ilen; k++ )
1008// fprintf( stderr, " %d", logClusters[k] );
1009// fprintf( stderr, "\n" );
1010 // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
1011 int charFrom = from - pos;
1012 if ( charFrom < 0 )
1013 charFrom = 0;
1014 int glyphStart = logClusters[charFrom];
1015 if ( charFrom > 0 && logClusters[charFrom-1] == glyphStart )
1016 while ( charFrom < ilen && logClusters[charFrom] == glyphStart )
1017 charFrom++;
1018 if ( charFrom < ilen ) {
1019 glyphStart = logClusters[charFrom];
1020 int charEnd = from + len - 1 - pos;
1021 if ( charEnd >= ilen )
1022 charEnd = ilen-1;
1023 int glyphEnd = logClusters[charEnd];
1024 while ( charEnd < ilen && logClusters[charEnd] == glyphEnd )
1025 charEnd++;
1026 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
1027
1028// qDebug("char: start=%d end=%d / glyph: start = %d, end = %d", charFrom, charEnd, glyphStart, glyphEnd );
1029 for ( int i = glyphStart; i < glyphEnd; i++ )
1030 w += advances[i];
1031 }
1032 }
1033 }
1034// qDebug(" --> w= %d ", w );
1035 return w;
1036}
1037
1038void QTextEngine::itemize( int mode )
1039{
1040 if ( !items.d ) {
1041 int size = 8;
1042 items.d = (QScriptItemArrayPrivate *)malloc( sizeof( QScriptItemArrayPrivate ) +
1043 sizeof( QScriptItem ) * size );
1044 items.d->alloc = size;
1045 }
1046 items.d->size = 0;
1047 if ( string.length() == 0 )
1048 return;
1049
1050 if ( !(mode & NoBidi) ) {
1051 if ( direction == QChar::DirON )
1052 direction = basicDirection( string );
1053 bidiItemize( this, direction == QChar::DirR, mode );
1054 } else {
1055 BidiControl control( FALSE );
1056 if ( mode & QTextEngine::SingleLine )
1057 control.singleLine = TRUE;
1058 int start = 0;
1059 int stop = string.length() - 1;
1060 appendItems(this, start, stop, control, QChar::DirL);
1061 }
1062 if ( (mode & WidthOnly) == WidthOnly )
1063 widthOnly = TRUE;
1064}
1065
1066glyph_metrics_t QTextEngine::boundingBox( int from, int len ) const
1067{
1068 glyph_metrics_t gm;
1069
1070 for ( int i = 0; i < items.size(); i++ ) {
1071 QScriptItem *si = &items[i];
1072 int pos = si->position;
1073 int ilen = length( i );
1074 if ( pos > from + len )
1075 break;
1076 if ( pos + len > from ) {
1077 if ( !si->num_glyphs )
1078 shape( i );
1079 advance_t *advances = this->advances( si );
1080 unsigned short *logClusters = this->logClusters( si );
1081 glyph_t *glyphs = this->glyphs( si );
1082 qoffset_t *offsets = this->offsets( si );
1083
1084 // do the simple thing for now and give the first glyph in a cluster the full width, all other ones 0.
1085 int charFrom = from - pos;
1086 if ( charFrom < 0 )
1087 charFrom = 0;
1088 int glyphStart = logClusters[charFrom];
1089 if ( charFrom > 0 && logClusters[charFrom-1] == glyphStart )
1090 while ( charFrom < ilen && logClusters[charFrom] == glyphStart )
1091 charFrom++;
1092 if ( charFrom < ilen ) {
1093 glyphStart = logClusters[charFrom];
1094 int charEnd = from + len - 1 - pos;
1095 if ( charEnd >= ilen )
1096 charEnd = ilen-1;
1097 int glyphEnd = logClusters[charEnd];
1098 while ( charEnd < ilen && logClusters[charEnd] == glyphEnd )
1099 charEnd++;
1100 glyphEnd = (charEnd == ilen) ? si->num_glyphs : logClusters[charEnd];
1101 if ( glyphStart <= glyphEnd ) {
1102 QFontEngine *fe = si->fontEngine;
1103 glyph_metrics_t m = fe->boundingBox( glyphs+glyphStart, advances+glyphStart,
1104 offsets+glyphStart, glyphEnd-glyphStart );
1105 gm.x = QMIN( gm.x, m.x + gm.xoff );
1106 gm.y = QMIN( gm.y, m.y + gm.yoff );
1107 gm.width = QMAX( gm.width, m.width+gm.xoff );
1108 gm.height = QMAX( gm.height, m.height+gm.yoff );
1109 gm.xoff += m.xoff;
1110 gm.yoff += m.yoff;
1111 }
1112 }
1113 }
1114 }
1115 return gm;
1116}
Note: See TracBrowser for help on using the repository browser.