source: trunk/src/kernel/qscriptengine.cpp@ 102

Last change on this file since 102 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.7 KB
Line 
1/****************************************************************************
2** $Id: qscriptengine.cpp 8 2005-11-16 19:36:46Z dmik $
3**
4** ???
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 "qscriptengine_p.h"
37
38#include "qstring.h"
39#include "qrect.h"
40#include "qfont.h"
41#include <private/qunicodetables_p.h>
42#include "qtextengine_p.h"
43#include "qfontengine_p.h"
44#include <stdlib.h>
45
46
47#undef None
48#undef Pre
49#undef Above
50#undef Below
51
52// --------------------------------------------------------------------------------------------------------------------------------------------
53//
54// Basic processing
55//
56// --------------------------------------------------------------------------------------------------------------------------------------------
57
58static inline void positionCluster( QTextEngine *engine, QScriptItem *si, int gfrom, int glast )
59{
60 si->hasPositioning = TRUE;
61 int nmarks = glast - gfrom;
62 if ( nmarks <= 0 ) {
63 qWarning( "positionCluster: no marks to position!" );
64 return;
65 }
66
67 glyph_t *glyphs = engine->glyphs( si );
68 qoffset_t *offsets = engine->offsets( si );
69 GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
70
71 QFontEngine *f = si->fontEngine;
72 glyph_metrics_t baseInfo = f->boundingBox( glyphs[gfrom] );
73
74 if ( si->analysis.script == QFont::Hebrew ) {
75 // we need to attach below the baseline, because of the hebrew iud.
76 baseInfo.height= QMAX( baseInfo.height, -baseInfo.y );
77 }
78 QRect baseRect( baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height );
79
80// qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast );
81// qDebug( "baseInfo: %d/%d (%d/%d) off=%d/%d", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff );
82
83 int size = f->ascent()/10;
84 int offsetBase = (size - 4) / 4 + QMIN( size, 4 ) + 1;
85// qDebug("offset = %d", offsetBase );
86
87 bool rightToLeft = si->analysis.bidiLevel % 2;
88
89 int i;
90 int lastCmb = -1;
91 QRect attachmentRect;
92
93 for( i = 1; i <= nmarks; i++ ) {
94 glyph_t mark = glyphs[gfrom+i];
95 QPoint p;
96 glyph_metrics_t markInfo = f->boundingBox( mark );
97 QRect markRect( markInfo.x, markInfo.y, markInfo.width, markInfo.height );
98
99 int offset = offsetBase;
100 unsigned char cmb = glyphAttributes[gfrom+i].combiningClass;
101
102 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
103 // bits in the glyphAttributes structure.
104 if ( cmb < 200 ) {
105 // fixed position classes. We approximate by mapping to one of the others.
106 // currently I added only the ones for arabic, hebrew, lao and thai.
107
108 // for Lao and Thai marks with class 0, see below ( heuristicSetGlyphAttributes )
109
110 // add a bit more offset to arabic, a bit hacky
111 if ( cmb >= 27 && cmb <= 36 && offset < 3 )
112 offset +=1;
113 // below
114 if ( (cmb >= 10 && cmb <= 18) ||
115 cmb == 20 || cmb == 22 ||
116 cmb == 29 || cmb == 32 )
117 cmb = QChar::Combining_Below;
118 // above
119 else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
120 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
121 cmb = QChar::Combining_Above;
122 //below-right
123 else if ( cmb == 9 || cmb == 103 || cmb == 118 )
124 cmb = QChar::Combining_BelowRight;
125 // above-right
126 else if ( cmb == 24 || cmb == 107 || cmb == 122 )
127 cmb = QChar::Combining_AboveRight;
128 else if ( cmb == 25 )
129 cmb = QChar::Combining_AboveLeft;
130 // fixed:
131 // 19 21
132
133 }
134
135 // combining marks of different class don't interact. Reset the rectangle.
136 if ( cmb != lastCmb ) {
137 //qDebug( "resetting rect" );
138 attachmentRect = baseRect;
139 }
140
141 switch( cmb ) {
142 case QChar::Combining_DoubleBelow:
143 // ### wrong in rtl context!
144 case QChar::Combining_BelowLeft:
145 p += QPoint( 0, offset );
146 case QChar::Combining_BelowLeftAttached:
147 p += attachmentRect.bottomLeft() - markRect.topLeft();
148 break;
149 case QChar::Combining_Below:
150 p += QPoint( 0, offset );
151 case QChar::Combining_BelowAttached:
152 p += attachmentRect.bottomLeft() - markRect.topLeft();
153 p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
154 break;
155 case QChar::Combining_BelowRight:
156 p += QPoint( 0, offset );
157 case QChar::Combining_BelowRightAttached:
158 p += attachmentRect.bottomRight() - markRect.topRight();
159 break;
160 case QChar::Combining_Left:
161 p += QPoint( -offset, 0 );
162 case QChar::Combining_LeftAttached:
163 break;
164 case QChar::Combining_Right:
165 p += QPoint( offset, 0 );
166 case QChar::Combining_RightAttached:
167 break;
168 case QChar::Combining_DoubleAbove:
169 // ### wrong in RTL context!
170 case QChar::Combining_AboveLeft:
171 p += QPoint( 0, -offset );
172 case QChar::Combining_AboveLeftAttached:
173 p += attachmentRect.topLeft() - markRect.bottomLeft();
174 break;
175 case QChar::Combining_Above:
176 p += QPoint( 0, -offset );
177 case QChar::Combining_AboveAttached:
178 p += attachmentRect.topLeft() - markRect.bottomLeft();
179 p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
180 break;
181 case QChar::Combining_AboveRight:
182 p += QPoint( 0, -offset );
183 case QChar::Combining_AboveRightAttached:
184 p += attachmentRect.topRight() - markRect.bottomRight();
185 break;
186
187 case QChar::Combining_IotaSubscript:
188 default:
189 break;
190 }
191// qDebug( "char=%x combiningClass = %d offset=%d/%d", mark, cmb, p.x(), p.y() );
192 markRect.moveBy( p.x(), p.y() );
193 attachmentRect |= markRect;
194 lastCmb = cmb;
195 if ( rightToLeft ) {
196 offsets[gfrom+i].x = p.x();
197 offsets[gfrom+i].y = p.y() - baseInfo.yoff;
198 } else {
199 offsets[gfrom+i].x = p.x() - baseInfo.xoff;
200 offsets[gfrom+i].y = p.y() - baseInfo.yoff;
201 }
202 }
203}
204
205
206void q_heuristicPosition( QTextEngine *engine, QScriptItem *si )
207{
208 GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
209
210 int cEnd = -1;
211 int i = si->num_glyphs;
212 while ( i-- ) {
213 if ( cEnd == -1 && glyphAttributes[i].mark ) {
214 cEnd = i;
215 } else if ( cEnd != -1 && !glyphAttributes[i].mark ) {
216 positionCluster( engine, si, i, cEnd );
217 cEnd = -1;
218 }
219 }
220}
221
222
223
224// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars ang glyphs
225// and no reordering.
226// also computes logClusters heuristically
227static void heuristicSetGlyphAttributes( const QString &string, int from, int len,
228 QTextEngine *engine, QScriptItem *si )
229{
230 // ### zeroWidth and justification are missing here!!!!!
231
232 if ( si->num_glyphs != len )
233 qWarning("QScriptEngine::heuristicSetGlyphAttributes: char length and num glyphs disagree" );
234
235// qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", num_glyphs);
236 GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
237 unsigned short *logClusters = engine->logClusters( si );
238 advance_t *advances = engine->advances( si );
239
240 // honour the logClusters array if it exists.
241 const QChar *uc = string.unicode() + from;
242
243 for ( int i = 0; i < si->num_glyphs; i++ )
244 logClusters[i] = i;
245
246 int pos = 1;
247
248 // first char in a run is never (treated as) a mark
249 int cStart = 0;
250 glyphAttributes[0].mark = FALSE;
251 glyphAttributes[0].clusterStart = TRUE;
252
253 while ( pos < len ) {
254 if ( ::category( uc[pos] ) != QChar::Mark_NonSpacing ) {
255 glyphAttributes[pos].mark = FALSE;
256 glyphAttributes[pos].clusterStart = TRUE;
257 glyphAttributes[pos].combiningClass = 0;
258 cStart = pos;
259 } else {
260 int cmb = combiningClass( uc[pos] );
261
262 if ( cmb == 0 ) {
263 // Fix 0 combining classes
264 if ( uc[pos].row() == 0x0e ) {
265 // thai or lao
266 unsigned char col = uc[pos].cell();
267 if ( col == 0x31 ||
268 col == 0x34 ||
269 col == 0x35 ||
270 col == 0x36 ||
271 col == 0x37 ||
272 col == 0x47 ||
273 col == 0x4c ||
274 col == 0x4d ||
275 col == 0x4e ) {
276 cmb = QChar::Combining_AboveRight;
277 } else if ( col == 0xb1 ||
278 col == 0xb4 ||
279 col == 0xb5 ||
280 col == 0xb6 ||
281 col == 0xb7 ||
282 col == 0xbb ||
283 col == 0xcc ||
284 col == 0xcd ) {
285 cmb = QChar::Combining_Above;
286 } else if ( col == 0xbc ) {
287 cmb = QChar::Combining_Below;
288 }
289 }
290 }
291
292 glyphAttributes[pos].mark = TRUE;
293 glyphAttributes[pos].clusterStart = FALSE;
294 glyphAttributes[pos].combiningClass = cmb;
295 // qDebug("found a mark at position %d", pos );
296 logClusters[pos] = cStart;
297 advances[pos] = 0;
298 si->hasPositioning = TRUE;
299 }
300 pos++;
301 }
302}
303
304static void convertToCMap( const QChar *chars, int len, QTextEngine *engine, QScriptItem *si )
305{
306 glyph_t *glyphs = engine->glyphs( si );
307 advance_t *advances = engine->advances( si );
308
309 si->num_glyphs = len;
310 engine->ensureSpace( len );
311 int error = si->fontEngine->stringToCMap( chars, len, glyphs, advances, &si->num_glyphs, (si->analysis.bidiLevel %2) );
312 if ( error == QFontEngine::OutOfMemory ) {
313 engine->ensureSpace( si->num_glyphs );
314 si->fontEngine->stringToCMap( chars, len, glyphs, advances, &si->num_glyphs, (si->analysis.bidiLevel %2) );
315 }
316}
317
318static void basic_shape( int /*script*/, const QString &string, int from, int len, QTextEngine *engine, QScriptItem *si )
319{
320 convertToCMap( string.unicode() + from, len, engine, si );
321 heuristicSetGlyphAttributes( string, from, len, engine, si );
322 if ( !engine->widthOnly ) {
323 q_heuristicPosition( engine, si );
324 }
325}
326
327static void basic_attributes( int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes )
328{
329 const QChar *uc = text.unicode() + from;
330 attributes += from;
331
332 QCharAttributes *a = attributes;
333
334 for ( int i = 0; i < len; i++ ) {
335 QChar::Category cat = ::category( *uc );
336 a->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
337 a->softBreak = FALSE;
338 a->charStop = (cat != QChar::Mark_NonSpacing);
339 a->wordStop = FALSE;
340 a->invalid = FALSE;
341 ++uc;
342 ++a;
343 }
344}
345
346// --------------------------------------------------------------------------------------------------------------------------------------------
347//
348// Middle eastern languages
349//
350// --------------------------------------------------------------------------------------------------------------------------------------------
351
352/*
353 Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
354 arabic).
355
356 Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
357 transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
358
359 Right join-causing: dual + center
360 Left join-causing: dual + right + center
361
362 Rules are as follows (for a string already in visual order, as we have it here):
363
364 R1 Transparent characters do not affect joining behaviour.
365 R2 A right joining character, that has a right join-causing char on the right will get form XRight
366 (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
367 Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
368 R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
369 the right will get form XMedial
370 R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
371 will get form XRight
372 R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
373 will get form XLeft
374 R7 Otherwise the character will get form XIsolated
375
376 Additionally we have to do the minimal ligature support for lam-alef ligatures:
377
378 L1 Transparent characters do not affect ligature behaviour.
379 L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
380 L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
381
382 The two functions defined in this class do shaping in visual and logical order. For logical order just replace right with
383 previous and left with next in the above rules ;-)
384*/
385
386/*
387 Two small helper functions for arabic shaping. They get the next shape causing character on either
388 side of the char in question. Implements rule R1.
389
390 leftChar() returns true if the char to the left is a left join-causing char
391 rightChar() returns true if the char to the right is a right join-causing char
392*/
393
394
395enum Shape {
396 XIsolated,
397 XFinal,
398 XInitial,
399 XMedial
400};
401
402/*
403 Two small helper functions for arabic shaping. They get the next shape causing character on either
404 side of the char in question. Implements rule R1.
405
406 leftChar() returns true if the char to the left is a left join-causing char
407 rightChar() returns true if the char to the right is a right join-causing char
408*/
409static inline const QChar *prevChar( const QString &str, int pos )
410{
411 //qDebug("leftChar: pos=%d", pos);
412 pos--;
413 const QChar *ch = str.unicode() + pos;
414 while( pos > -1 ) {
415 if( ::category( *ch ) != QChar::Mark_NonSpacing )
416 return ch;
417 pos--;
418 ch--;
419 }
420 return &QChar::replacement;
421}
422
423static inline const QChar *nextChar( const QString &str, int pos)
424{
425 pos++;
426 int len = str.length();
427 const QChar *ch = str.unicode() + pos;
428 while( pos < len ) {
429 //qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining());
430 if( ::category( *ch ) != QChar::Mark_NonSpacing )
431 return ch;
432 // assume it's a transparent char, this might not be 100% correct
433 pos++;
434 ch++;
435 }
436 return &QChar::replacement;
437}
438
439/* and the same thing for logical ordering :)
440 */
441static inline bool prevLogicalCharJoins( const QString &str, int pos)
442{
443 return ( joining( *nextChar( str, pos ) ) != QChar::OtherJoining );
444}
445
446static inline bool nextLogicalCharJoins( const QString &str, int pos)
447{
448 QChar::Joining join = joining( *prevChar( str, pos ) );
449 return ( join == QChar::Dual || join == QChar::Center );
450}
451
452
453static inline Shape glyphVariantLogical( const QString &str, int pos)
454{
455 QChar::Joining joining = ::joining( str.unicode()[pos] );
456 //qDebug("checking %x, joining=%d", str[pos].unicode(), joining);
457 switch ( joining ) {
458 case QChar::OtherJoining:
459 case QChar::Center:
460 // these don't change shape
461 return XIsolated;
462 case QChar::Right:
463 // only rule R2 applies
464 return ( nextLogicalCharJoins( str, pos ) ) ? XFinal : XIsolated;
465 case QChar::Dual:
466 bool right = nextLogicalCharJoins( str, pos );
467 bool left = prevLogicalCharJoins( str, pos );
468 //qDebug("dual: right=%d, left=%d", right, left);
469 return ( right ) ? ( left ? XMedial : XFinal ) : ( left ? XInitial : XIsolated );
470 }
471 return XIsolated;
472}
473
474
475// The unicode to unicode shaping codec.
476// does only presentation forms B at the moment, but that should be enough for
477// simple display
478static const ushort arabicUnicodeMapping[256][2] = {
479 // base of shaped forms, and number-1 of them ( 0 for non shaping,
480 // 1 for right binding and 3 for dual binding
481
482 // These are just the glyphs available in Unicode,
483 // some characters are in R class, but have no glyphs in Unicode.
484
485 { 0x0600, 0 }, // 0x0600
486 { 0x0601, 0 }, // 0x0601
487 { 0x0602, 0 }, // 0x0602
488 { 0x0603, 0 }, // 0x0603
489 { 0x0604, 0 }, // 0x0604
490 { 0x0605, 0 }, // 0x0605
491 { 0x0606, 0 }, // 0x0606
492 { 0x0607, 0 }, // 0x0607
493 { 0x0608, 0 }, // 0x0608
494 { 0x0609, 0 }, // 0x0609
495 { 0x060A, 0 }, // 0x060A
496 { 0x060B, 0 }, // 0x060B
497 { 0x060C, 0 }, // 0x060C
498 { 0x060D, 0 }, // 0x060D
499 { 0x060E, 0 }, // 0x060E
500 { 0x060F, 0 }, // 0x060F
501
502 { 0x0610, 0 }, // 0x0610
503 { 0x0611, 0 }, // 0x0611
504 { 0x0612, 0 }, // 0x0612
505 { 0x0613, 0 }, // 0x0613
506 { 0x0614, 0 }, // 0x0614
507 { 0x0615, 0 }, // 0x0615
508 { 0x0616, 0 }, // 0x0616
509 { 0x0617, 0 }, // 0x0617
510 { 0x0618, 0 }, // 0x0618
511 { 0x0619, 0 }, // 0x0619
512 { 0x061A, 0 }, // 0x061A
513 { 0x061B, 0 }, // 0x061B
514 { 0x061C, 0 }, // 0x061C
515 { 0x061D, 0 }, // 0x061D
516 { 0x061E, 0 }, // 0x061E
517 { 0x061F, 0 }, // 0x061F
518
519 { 0x0620, 0 }, // 0x0620
520 { 0xFE80, 0 }, // 0x0621 HAMZA
521 { 0xFE81, 1 }, // 0x0622 R ALEF WITH MADDA ABOVE
522 { 0xFE83, 1 }, // 0x0623 R ALEF WITH HAMZA ABOVE
523 { 0xFE85, 1 }, // 0x0624 R WAW WITH HAMZA ABOVE
524 { 0xFE87, 1 }, // 0x0625 R ALEF WITH HAMZA BELOW
525 { 0xFE89, 3 }, // 0x0626 D YEH WITH HAMZA ABOVE
526 { 0xFE8D, 1 }, // 0x0627 R ALEF
527 { 0xFE8F, 3 }, // 0x0628 D BEH
528 { 0xFE93, 1 }, // 0x0629 R TEH MARBUTA
529 { 0xFE95, 3 }, // 0x062A D TEH
530 { 0xFE99, 3 }, // 0x062B D THEH
531 { 0xFE9D, 3 }, // 0x062C D JEEM
532 { 0xFEA1, 3 }, // 0x062D D HAH
533 { 0xFEA5, 3 }, // 0x062E D KHAH
534 { 0xFEA9, 1 }, // 0x062F R DAL
535
536 { 0xFEAB, 1 }, // 0x0630 R THAL
537 { 0xFEAD, 1 }, // 0x0631 R REH
538 { 0xFEAF, 1 }, // 0x0632 R ZAIN
539 { 0xFEB1, 3 }, // 0x0633 D SEEN
540 { 0xFEB5, 3 }, // 0x0634 D SHEEN
541 { 0xFEB9, 3 }, // 0x0635 D SAD
542 { 0xFEBD, 3 }, // 0x0636 D DAD
543 { 0xFEC1, 3 }, // 0x0637 D TAH
544 { 0xFEC5, 3 }, // 0x0638 D ZAH
545 { 0xFEC9, 3 }, // 0x0639 D AIN
546 { 0xFECD, 3 }, // 0x063A D GHAIN
547 { 0x063B, 0 }, // 0x063B
548 { 0x063C, 0 }, // 0x063C
549 { 0x063D, 0 }, // 0x063D
550 { 0x063E, 0 }, // 0x063E
551 { 0x063F, 0 }, // 0x063F
552
553 { 0x0640, 0 }, // 0x0640 C TATWEEL // ### Join Causing, only one glyph
554 { 0xFED1, 3 }, // 0x0641 D FEH
555 { 0xFED5, 3 }, // 0x0642 D QAF
556 { 0xFED9, 3 }, // 0x0643 D KAF
557 { 0xFEDD, 3 }, // 0x0644 D LAM
558 { 0xFEE1, 3 }, // 0x0645 D MEEM
559 { 0xFEE5, 3 }, // 0x0646 D NOON
560 { 0xFEE9, 3 }, // 0x0647 D HEH
561 { 0xFEED, 1 }, // 0x0648 R WAW
562 { 0x0649, 0 }, // 0x0649 ALEF MAKSURA // ### Dual, glyphs not consecutive, handle in code.
563 { 0xFEF1, 3 }, // 0x064A D YEH
564 { 0x064B, 0 }, // 0x064B
565 { 0x064C, 0 }, // 0x064C
566 { 0x064D, 0 }, // 0x064D
567 { 0x064E, 0 }, // 0x064E
568 { 0x064F, 0 }, // 0x064F
569
570 { 0x0650, 0 }, // 0x0650
571 { 0x0651, 0 }, // 0x0651
572 { 0x0652, 0 }, // 0x0652
573 { 0x0653, 0 }, // 0x0653
574 { 0x0654, 0 }, // 0x0654
575 { 0x0655, 0 }, // 0x0655
576 { 0x0656, 0 }, // 0x0656
577 { 0x0657, 0 }, // 0x0657
578 { 0x0658, 0 }, // 0x0658
579 { 0x0659, 0 }, // 0x0659
580 { 0x065A, 0 }, // 0x065A
581 { 0x065B, 0 }, // 0x065B
582 { 0x065C, 0 }, // 0x065C
583 { 0x065D, 0 }, // 0x065D
584 { 0x065E, 0 }, // 0x065E
585 { 0x065F, 0 }, // 0x065F
586
587 { 0x0660, 0 }, // 0x0660
588 { 0x0661, 0 }, // 0x0661
589 { 0x0662, 0 }, // 0x0662
590 { 0x0663, 0 }, // 0x0663
591 { 0x0664, 0 }, // 0x0664
592 { 0x0665, 0 }, // 0x0665
593 { 0x0666, 0 }, // 0x0666
594 { 0x0667, 0 }, // 0x0667
595 { 0x0668, 0 }, // 0x0668
596 { 0x0669, 0 }, // 0x0669
597 { 0x066A, 0 }, // 0x066A
598 { 0x066B, 0 }, // 0x066B
599 { 0x066C, 0 }, // 0x066C
600 { 0x066D, 0 }, // 0x066D
601 { 0x066E, 0 }, // 0x066E
602 { 0x066F, 0 }, // 0x066F
603
604 { 0x0670, 0 }, // 0x0670
605 { 0xFB50, 1 }, // 0x0671 R ALEF WASLA
606 { 0x0672, 0 }, // 0x0672
607 { 0x0673, 0 }, // 0x0673
608 { 0x0674, 0 }, // 0x0674
609 { 0x0675, 0 }, // 0x0675
610 { 0x0676, 0 }, // 0x0676
611 { 0x0677, 0 }, // 0x0677
612 { 0x0678, 0 }, // 0x0678
613 { 0xFB66, 3 }, // 0x0679 D TTEH
614 { 0xFB5E, 3 }, // 0x067A D TTEHEH
615 { 0xFB52, 3 }, // 0x067B D BEEH
616 { 0x067C, 0 }, // 0x067C
617 { 0x067D, 0 }, // 0x067D
618 { 0xFB56, 3 }, // 0x067E D PEH
619 { 0xFB62, 3 }, // 0x067F D TEHEH
620
621 { 0xFB5A, 3 }, // 0x0680 D BEHEH
622 { 0x0681, 0 }, // 0x0681
623 { 0x0682, 0 }, // 0x0682
624 { 0xFB76, 3 }, // 0x0683 D NYEH
625 { 0xFB72, 3 }, // 0x0684 D DYEH
626 { 0x0685, 0 }, // 0x0685
627 { 0xFB7A, 3 }, // 0x0686 D TCHEH
628 { 0xFB7E, 3 }, // 0x0687 D TCHEHEH
629 { 0xFB88, 1 }, // 0x0688 R DDAL
630 { 0x0689, 0 }, // 0x0689
631 { 0x068A, 0 }, // 0x068A
632 { 0x068B, 0 }, // 0x068B
633 { 0xFB84, 1 }, // 0x068C R DAHAL
634 { 0xFB82, 1 }, // 0x068D R DDAHAL
635 { 0xFB86, 1 }, // 0x068E R DUL
636 { 0x068F, 0 }, // 0x068F
637
638 { 0x0690, 0 }, // 0x0690
639 { 0xFB8C, 1 }, // 0x0691 R RREH
640 { 0x0692, 0 }, // 0x0692
641 { 0x0693, 0 }, // 0x0693
642 { 0x0694, 0 }, // 0x0694
643 { 0x0695, 0 }, // 0x0695
644 { 0x0696, 0 }, // 0x0696
645 { 0x0697, 0 }, // 0x0697
646 { 0xFB8A, 1 }, // 0x0698 R JEH
647 { 0x0699, 0 }, // 0x0699
648 { 0x069A, 0 }, // 0x069A
649 { 0x069B, 0 }, // 0x069B
650 { 0x069C, 0 }, // 0x069C
651 { 0x069D, 0 }, // 0x069D
652 { 0x069E, 0 }, // 0x069E
653 { 0x069F, 0 }, // 0x069F
654
655 { 0x06A0, 0 }, // 0x06A0
656 { 0x06A1, 0 }, // 0x06A1
657 { 0x06A2, 0 }, // 0x06A2
658 { 0x06A3, 0 }, // 0x06A3
659 { 0xFB6A, 3 }, // 0x06A4 D VEH
660 { 0x06A5, 0 }, // 0x06A5
661 { 0xFB6E, 3 }, // 0x06A6 D PEHEH
662 { 0x06A7, 0 }, // 0x06A7
663 { 0x06A8, 0 }, // 0x06A8
664 { 0xFB8E, 3 }, // 0x06A9 D KEHEH
665 { 0x06AA, 0 }, // 0x06AA
666 { 0x06AB, 0 }, // 0x06AB
667 { 0x06AC, 0 }, // 0x06AC
668 { 0xFBD3, 3 }, // 0x06AD D NG
669 { 0x06AE, 0 }, // 0x06AE
670 { 0xFB92, 3 }, // 0x06AF D GAF
671
672 { 0x06B0, 0 }, // 0x06B0
673 { 0xFB9A, 3 }, // 0x06B1 D NGOEH
674 { 0x06B2, 0 }, // 0x06B2
675 { 0xFB96, 3 }, // 0x06B3 D GUEH
676 { 0x06B4, 0 }, // 0x06B4
677 { 0x06B5, 0 }, // 0x06B5
678 { 0x06B6, 0 }, // 0x06B6
679 { 0x06B7, 0 }, // 0x06B7
680 { 0x06B8, 0 }, // 0x06B8
681 { 0x06B9, 0 }, // 0x06B9
682 { 0x06BA, 0 }, // 0x06BA
683 { 0xFBA0, 3 }, // 0x06BB D RNOON
684 { 0x06BC, 0 }, // 0x06BC
685 { 0x06BD, 0 }, // 0x06BD
686 { 0xFBAA, 3 }, // 0x06BE D HEH DOACHASHMEE
687 { 0x06BF, 0 }, // 0x06BF
688
689 { 0xFBA4, 1 }, // 0x06C0 R HEH WITH YEH ABOVE
690 { 0xFBA6, 3 }, // 0x06C1 D HEH GOAL
691 { 0x06C2, 0 }, // 0x06C2
692 { 0x06C3, 0 }, // 0x06C3
693 { 0x06C4, 0 }, // 0x06C4
694 { 0xFBE0, 1 }, // 0x06C5 R KIRGHIZ OE
695 { 0xFBD9, 1 }, // 0x06C6 R OE
696 { 0xFBD7, 1 }, // 0x06C7 R U
697 { 0xFBDB, 1 }, // 0x06C8 R YU
698 { 0xFBE2, 1 }, // 0x06C9 R KIRGHIZ YU
699 { 0x06CA, 0 }, // 0x06CA
700 { 0xFBDE, 1 }, // 0x06CB R VE
701 { 0xFBFC, 3 }, // 0x06CC D FARSI YEH
702 { 0x06CD, 0 }, // 0x06CD
703 { 0x06CE, 0 }, // 0x06CE
704 { 0x06CF, 0 }, // 0x06CF
705
706 { 0xFBE4, 3 }, // 0x06D0 D E
707 { 0x06D1, 0 }, // 0x06D1
708 { 0xFBAE, 1 }, // 0x06D2 R YEH BARREE
709 { 0xFBB0, 1 }, // 0x06D3 R YEH BARREE WITH HAMZA ABOVE
710 { 0x06D4, 0 }, // 0x06D4
711 { 0x06D5, 0 }, // 0x06D5
712 { 0x06D6, 0 }, // 0x06D6
713 { 0x06D7, 0 }, // 0x06D7
714 { 0x06D8, 0 }, // 0x06D8
715 { 0x06D9, 0 }, // 0x06D9
716 { 0x06DA, 0 }, // 0x06DA
717 { 0x06DB, 0 }, // 0x06DB
718 { 0x06DC, 0 }, // 0x06DC
719 { 0x06DD, 0 }, // 0x06DD
720 { 0x06DE, 0 }, // 0x06DE
721 { 0x06DF, 0 }, // 0x06DF
722
723 { 0x06E0, 0 }, // 0x06E0
724 { 0x06E1, 0 }, // 0x06E1
725 { 0x06E2, 0 }, // 0x06E2
726 { 0x06E3, 0 }, // 0x06E3
727 { 0x06E4, 0 }, // 0x06E4
728 { 0x06E5, 0 }, // 0x06E5
729 { 0x06E6, 0 }, // 0x06E6
730 { 0x06E7, 0 }, // 0x06E7
731 { 0x06E8, 0 }, // 0x06E8
732 { 0x06E9, 0 }, // 0x06E9
733 { 0x06EA, 0 }, // 0x06EA
734 { 0x06EB, 0 }, // 0x06EB
735 { 0x06EC, 0 }, // 0x06EC
736 { 0x06ED, 0 }, // 0x06ED
737 { 0x06EE, 0 }, // 0x06EE
738 { 0x06EF, 0 }, // 0x06EF
739
740 { 0x06F0, 0 }, // 0x06F0
741 { 0x06F1, 0 }, // 0x06F1
742 { 0x06F2, 0 }, // 0x06F2
743 { 0x06F3, 0 }, // 0x06F3
744 { 0x06F4, 0 }, // 0x06F4
745 { 0x06F5, 0 }, // 0x06F5
746 { 0x06F6, 0 }, // 0x06F6
747 { 0x06F7, 0 }, // 0x06F7
748 { 0x06F8, 0 }, // 0x06F8
749 { 0x06F9, 0 }, // 0x06F9
750 { 0x06FA, 0 }, // 0x06FA
751 { 0x06FB, 0 }, // 0x06FB
752 { 0x06FC, 0 }, // 0x06FC
753 { 0x06FD, 0 }, // 0x06FD
754 { 0x06FE, 0 }, // 0x06FE
755 { 0x06FF, 0 } // 0x06FF
756};
757
758// the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, this table does
759static const ushort alefMaksura[4] = {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9};
760
761// this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
762// of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
763// medial to the final form
764static const ushort arabicUnicodeLamAlefMapping[6][4] = {
765 { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, // 0x622 R Alef with Madda above
766 { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, // 0x623 R Alef with Hamza above
767 { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x624 // Just to fill the table ;-)
768 { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, // 0x625 R Alef with Hamza below
769 { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x626 // Just to fill the table ;-)
770 { 0xfffd, 0xfffd, 0xfefb, 0xfefc } // 0x627 R Alef
771};
772
773static inline int getShape( uchar cell, int shape )
774{
775#ifdef Q_OS_MAC
776 // the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here
777 uint ch = ( cell != 0x49 )
778 ? (shape ? arabicUnicodeMapping[cell][0] + shape : 0x600+cell)
779 : alefMaksura[shape] ;
780#else
781 // the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here
782 uint ch = ( cell != 0x49 ) ? arabicUnicodeMapping[cell][0] + shape
783 : alefMaksura[shape] ;
784#endif
785 return ch;
786}
787
788
789static void shapedString(const QString& uc, int from, int len, QChar *shapeBuffer, int *shapedLength,
790 bool reverse, GlyphAttributes *attrs, unsigned short *logClusters )
791{
792 if( len < 0 ) {
793 len = uc.length() - from;
794 } else if( len == 0 ) {
795 *shapedLength = 0;
796 return;
797 }
798
799 const QChar *ch = uc.unicode() + from;
800 QChar *data = shapeBuffer;
801 int clusterStart = 0;
802
803 for ( int i = 0; i < len; i++ ) {
804 uchar r = ch->row();
805 int gpos = data - shapeBuffer;
806
807 if ( r != 0x06 ) {
808 if ( r == 0x20 ) {
809 uchar c = ch->cell();
810 if (c == 0x0c || c == 0x0d)
811 // remove ZWJ and ZWNJ
812 goto skip;
813 }
814 if ( reverse )
815 *data = mirroredChar( *ch );
816 else
817 *data = *ch;
818 } else {
819 uchar c = ch->cell();
820 int pos = i + from;
821 int shape = glyphVariantLogical( uc, pos );
822// qDebug("mapping U+%x to shape %d glyph=0x%x", ch->unicode(), shape, getShape(c, shape));
823 // take care of lam-alef ligatures (lam right of alef)
824 ushort map;
825 switch ( c ) {
826 case 0x44: { // lam
827 const QChar *pch = nextChar( uc, pos );
828 if ( pch->row() == 0x06 ) {
829 switch ( pch->cell() ) {
830 case 0x22:
831 case 0x23:
832 case 0x25:
833 case 0x27:
834// qDebug(" lam of lam-alef ligature");
835 map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
836 goto next;
837 default:
838 break;
839 }
840 }
841 break;
842 }
843 case 0x22: // alef with madda
844 case 0x23: // alef with hamza above
845 case 0x25: // alef with hamza below
846 case 0x27: // alef
847 if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
848 // have a lam alef ligature
849 //qDebug(" alef of lam-alef ligature");
850 goto skip;
851 }
852 default:
853 break;
854 }
855 map = getShape( c, shape );
856 next:
857 *data = map;
858 }
859 // #### Qt 4.0: Fixme
860 attrs[gpos].zeroWidth = false;
861 if ( ::category( *ch ) == QChar::Mark_NonSpacing ) {
862 attrs[gpos].mark = TRUE;
863// qDebug("glyph %d (char %d) is mark!", gpos, i );
864 } else {
865 attrs[gpos].mark = FALSE;
866 clusterStart = data - shapeBuffer;
867 }
868 attrs[gpos].clusterStart = !attrs[gpos].mark;
869 attrs[gpos].combiningClass = combiningClass( *ch );
870 data++;
871 skip:
872 ch++;
873 logClusters[i] = clusterStart;
874 }
875 *shapedLength = data - shapeBuffer;
876}
877
878#if defined( Q_WS_X11) && !defined( QT_NO_XFTFREETYPE )
879
880static void arabicSyriacOpenTypeShape( int script, QOpenType *openType, const QString &string, int from,
881 int len, QTextEngine *engine, QScriptItem *si )
882{
883 convertToCMap( string.unicode() + from, len, engine, si );
884 heuristicSetGlyphAttributes( string, from, len, engine, si );
885
886 unsigned char gv[256];
887 unsigned char *glyphVariant = gv;
888 bool ap[256];
889 bool *apply = ap;
890
891 if ( si->num_glyphs > 255 ) {
892 glyphVariant = (unsigned char *)malloc(si->num_glyphs*sizeof(unsigned char));
893 apply = (bool *)malloc(si->num_glyphs*sizeof(bool));
894 }
895
896 for ( int i = 0; i < si->num_glyphs; i++ )
897 glyphVariant[i] = glyphVariantLogical( string, from + i );
898
899 glyph_t *glyphs = engine->glyphs(si);
900 GlyphAttributes *attrs = engine->glyphAttributes(si);
901 unsigned short *logClusters = engine->logClusters(si);
902 unsigned short *uc = (unsigned short *)string.unicode() + from;
903
904 // Hack to remove ZWJ and ZWNJ from rendered output.
905 int j = 0;
906 for ( int i = 0; i < si->num_glyphs; i++ ) {
907 if (uc[i] == 0x200c || uc[i] == 0x200d)
908 continue;
909 glyphs[j] = glyphs[i];
910 attrs[j] = attrs[i];
911 glyphVariant[j] = glyphVariant[i];
912 logClusters[i] = logClusters[j];
913 ++j;
914 }
915 si->num_glyphs = j;
916
917 openType->init(glyphs, attrs, si->num_glyphs, logClusters, si->num_glyphs);
918
919 // call features in the order defined by http://www.microsoft.com/typography/otfntdev/arabicot/shaping.htm
920 openType->applyGSUBFeature(FT_MAKE_TAG( 'c', 'c', 'm', 'p' ));
921
922 if (script == QFont::Arabic) {
923 const struct {
924 int tag;
925 int shape;
926 } features[] = {
927 { FT_MAKE_TAG( 'i', 's', 'o', 'l' ), XIsolated },
928 { FT_MAKE_TAG( 'f', 'i', 'n', 'a' ), XFinal },
929 { FT_MAKE_TAG( 'm', 'e', 'd', 'i' ), XMedial },
930 { FT_MAKE_TAG( 'i', 'n', 'i', 't' ), XInitial }
931 };
932 for (int j = 0; j < 4; ++j) {
933 for ( int i = 0; i < si->num_glyphs; i++ )
934 apply[i] = (glyphVariant[i] == features[j].shape);
935 openType->applyGSUBFeature(features[j].tag, apply);
936 }
937 } else {
938 const struct {
939 int tag;
940 int shape;
941 } features[] = {
942 { FT_MAKE_TAG( 'i', 's', 'o', 'l' ), XIsolated },
943 { FT_MAKE_TAG( 'f', 'i', 'n', 'a' ), XFinal },
944 { FT_MAKE_TAG( 'f', 'i', 'n', '2' ), XFinal },
945 { FT_MAKE_TAG( 'f', 'i', 'n', '3' ), XFinal },
946 { FT_MAKE_TAG( 'm', 'e', 'd', 'i' ), XMedial },
947 { FT_MAKE_TAG( 'm', 'e', 'd', '2' ), XMedial },
948 { FT_MAKE_TAG( 'i', 'n', 'i', 't' ), XInitial }
949 };
950 for (int j = 0; j < 7; ++j) {
951 for ( int i = 0; i < si->num_glyphs; i++ )
952 apply[i] = (glyphVariant[i] == features[j].shape);
953 openType->applyGSUBFeature(features[j].tag, apply);
954 }
955 }
956 const int commonFeatures[] = {
957 // these features get applied to all glyphs and both scripts
958 FT_MAKE_TAG( 'r', 'l', 'i', 'g' ),
959 FT_MAKE_TAG( 'c', 'a', 'l', 't' ),
960 FT_MAKE_TAG( 'l', 'i', 'g', 'a' ),
961 FT_MAKE_TAG( 'd', 'l', 'i', 'g' )
962 };
963 for (int j = 0; j < 4; ++j)
964 openType->applyGSUBFeature(commonFeatures[j]);
965
966 if (script == QFont::Arabic) {
967 const int features[] = {
968 FT_MAKE_TAG( 'c', 's', 'w', 'h' ),
969 // mset is used in old Win95 fonts that don't have a 'mark' positioning table.
970 FT_MAKE_TAG( 'm', 's', 'e', 't' )
971 };
972 for (int j = 0; j < 2; ++j)
973 openType->applyGSUBFeature(features[j]);
974 }
975
976 openType->applyGPOSFeatures();
977 si->num_glyphs = 0;
978 openType->appendTo(engine, si);
979
980 if (glyphVariant != gv) {
981 free(glyphVariant);
982 free(apply);
983 }
984}
985
986#endif
987
988static void arabic_attributes( int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes )
989{
990 const QChar *uc = text.unicode() + from;
991 attributes += from;
992 for ( int i = 0; i < len; i++ ) {
993 QChar::Category cat = ::category( *uc );
994 attributes->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
995 attributes->softBreak = FALSE;
996 attributes->charStop = (cat != QChar::Mark_NonSpacing);
997 attributes->wordStop = FALSE;
998 attributes->invalid = FALSE;
999 ++uc;
1000 ++attributes;
1001 }
1002}
1003
1004
1005// #### stil missing: identify invalid character combinations
1006static void arabic_shape( int /*script*/, const QString &string, int from, int len,
1007 QTextEngine *engine, QScriptItem *si )
1008{
1009#if defined( Q_WS_X11) && !defined( QT_NO_XFTFREETYPE )
1010 QOpenType *openType = si->fontEngine->openType();
1011
1012 if ( openType && openType->supportsScript( QFont::Arabic ) ) {
1013 arabicSyriacOpenTypeShape( QFont::Arabic, openType, string, from, len, engine, si );
1014 return;
1015 }
1016#endif
1017
1018 const QString &text = string;
1019
1020 GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
1021 unsigned short *logClusters = engine->logClusters( si );
1022
1023 QChar *shapedChars = (QChar *)::malloc( len * sizeof( QChar ) );
1024
1025 int slen;
1026 shapedString( text, from, len, shapedChars, &slen, (si->analysis.bidiLevel%2),
1027 glyphAttributes, logClusters );
1028
1029 convertToCMap( shapedChars, slen, engine, si );
1030 advance_t *advances = engine->advances(si);
1031 for (int i = 0; i < slen; ++i)
1032 if (glyphAttributes[i].mark)
1033 advances[i] = 0;
1034
1035 ::free( shapedChars );
1036 q_heuristicPosition( engine, si );
1037}
1038
1039#ifdef Q_WS_X11
1040# include "qscriptengine_x11.cpp"
1041#elif defined( Q_WS_WIN )
1042# include "qscriptengine_win.cpp"
1043#elif defined( Q_WS_PM )
1044# include "qscriptengine_pm.cpp"
1045#elif defined( Q_WS_MAC )
1046# include "qscriptengine_mac.cpp"
1047#elif defined( Q_WS_QWS )
1048# include "qscriptengine_qws.cpp"
1049#endif
Note: See TracBrowser for help on using the repository browser.