[2] | 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 |
|
---|
| 58 | static 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 |
|
---|
| 206 | void 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
|
---|
| 227 | static 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 |
|
---|
| 304 | static 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 |
|
---|
| 318 | static 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 |
|
---|
| 327 | static 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 |
|
---|
| 395 | enum 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 | */
|
---|
| 409 | static 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 |
|
---|
| 423 | static 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 | */
|
---|
| 441 | static inline bool prevLogicalCharJoins( const QString &str, int pos)
|
---|
| 442 | {
|
---|
| 443 | return ( joining( *nextChar( str, pos ) ) != QChar::OtherJoining );
|
---|
| 444 | }
|
---|
| 445 |
|
---|
| 446 | static 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 |
|
---|
| 453 | static 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
|
---|
| 478 | static 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
|
---|
| 759 | static 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
|
---|
| 764 | static 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 |
|
---|
| 773 | static 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 |
|
---|
| 789 | static 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 |
|
---|
| 880 | static 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 |
|
---|
| 988 | static 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
|
---|
| 1006 | static 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"
|
---|
[8] | 1043 | #elif defined( Q_WS_PM )
|
---|
| 1044 | # include "qscriptengine_pm.cpp"
|
---|
[2] | 1045 | #elif defined( Q_WS_MAC )
|
---|
| 1046 | # include "qscriptengine_mac.cpp"
|
---|
| 1047 | #elif defined( Q_WS_QWS )
|
---|
| 1048 | # include "qscriptengine_qws.cpp"
|
---|
| 1049 | #endif
|
---|