source: trunk/src/kernel/qtextengine.cpp

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

Transferred Qt for OS/2 version 3.3.1-rc5 sources from the CVS

  • Property svn:keywords set to Id
File size: 34.4 KB
Line 
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
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_PM )
829# include "qtextengine_pm.cpp"
830#elif defined( Q_WS_MAC )
831# include "qtextengine_mac.cpp"
832#endif
833
834
835
836QTextEngine::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
873QTextEngine::~QTextEngine()
874{
875 if ( fnt && fnt->deref())
876 delete fnt;
877 free( memory );
878 allocated = 0;
879}
880
881void 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
919const 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
942void 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
989int 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
1040void 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
1068glyph_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}
Note: See TracBrowser for help on using the repository browser.