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 |
|
---|
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"
|
---|
828 | #elif defined( Q_WS_MAC )
|
---|
829 | # include "qtextengine_mac.cpp"
|
---|
830 | #endif
|
---|
831 |
|
---|
832 |
|
---|
833 |
|
---|
834 | QTextEngine::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 |
|
---|
871 | QTextEngine::~QTextEngine()
|
---|
872 | {
|
---|
873 | if ( fnt && fnt->deref())
|
---|
874 | delete fnt;
|
---|
875 | free( memory );
|
---|
876 | allocated = 0;
|
---|
877 | }
|
---|
878 |
|
---|
879 | void 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 |
|
---|
917 | const 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 |
|
---|
940 | void 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 |
|
---|
987 | int 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 |
|
---|
1038 | void 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 |
|
---|
1066 | glyph_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 | }
|
---|