~oif-team/ubuntu/natty/qt4-x11/xi2.1

« back to all changes in this revision

Viewing changes to src/gui/text/qscriptengine.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-08-24 04:09:09 UTC
  • Revision ID: james.westby@ubuntu.com-20050824040909-xmxe9jfr4a0w5671
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
 
4
**
 
5
** This file is part of the text module of the Qt Toolkit.
 
6
**
 
7
** This file may be distributed under the terms of the Q Public License
 
8
** as defined by Trolltech AS of Norway and appearing in the file
 
9
** LICENSE.QPL included in the packaging of this file.
 
10
**
 
11
** This file may be distributed and/or modified under the terms of the
 
12
** GNU General Public License version 2 as published by the Free Software
 
13
** Foundation and appearing in the file LICENSE.GPL included in the
 
14
** packaging of this file.
 
15
**
 
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
 
17
**   information about Qt Commercial License Agreements.
 
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
 
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
 
20
**
 
21
** Contact info@trolltech.com if any conditions of this licensing are
 
22
** not clear to you.
 
23
**
 
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 
26
**
 
27
****************************************************************************/
 
28
 
 
29
#include "qscriptengine_p.h"
 
30
 
 
31
#include "qstring.h"
 
32
#include "qrect.h"
 
33
#include "qfont.h"
 
34
#include <private/qunicodetables_p.h>
 
35
#include "qtextengine_p.h"
 
36
#include "qfontengine_p.h"
 
37
#include <stdlib.h>
 
38
#include <qvarlengtharray.h>
 
39
#ifdef QT_HAVE_FREETYPE
 
40
#include "qopentype_p.h"
 
41
#endif
 
42
 
 
43
#undef None
 
44
#undef Pre
 
45
#undef Above
 
46
#undef Below
 
47
 
 
48
// --------------------------------------------------------------------------------------------------------------------------------------------
 
49
//
 
50
// Basic processing
 
51
//
 
52
// --------------------------------------------------------------------------------------------------------------------------------------------
 
53
 
 
54
static inline void positionCluster(QShaperItem *item, int gfrom,  int glast)
 
55
{
 
56
    int nmarks = glast - gfrom;
 
57
    if (nmarks <= 0) {
 
58
        qWarning("positionCluster: no marks to position!");
 
59
        return;
 
60
    }
 
61
 
 
62
    QGlyphLayout *glyphs = item->glyphs;
 
63
    QFontEngine *f = item->font;
 
64
 
 
65
    glyph_metrics_t baseInfo = f->boundingBox(glyphs[gfrom].glyph);
 
66
 
 
67
    if (item->script == QUnicodeTables::Hebrew)
 
68
        // we need to attach below the baseline, because of the hebrew iud.
 
69
        baseInfo.height = qMax(baseInfo.height, -baseInfo.y);
 
70
 
 
71
    QRectF baseRect(baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height);
 
72
 
 
73
//     qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
 
74
//     qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
 
75
 
 
76
    qreal size = f->ascent()/10.;
 
77
    qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
 
78
//     qDebug("offset = %f", offsetBase);
 
79
 
 
80
    bool rightToLeft = item->flags & QTextEngine::RightToLeft;
 
81
 
 
82
    int i;
 
83
    unsigned char lastCmb = 0;
 
84
    QRectF attachmentRect;
 
85
 
 
86
    for(i = 1; i <= nmarks; i++) {
 
87
        glyph_t mark = glyphs[gfrom+i].glyph;
 
88
        QPointF p;
 
89
        glyph_metrics_t markInfo = f->boundingBox(mark);
 
90
        QRectF markRect(markInfo.x, markInfo.y, markInfo.width, markInfo.height);
 
91
//         qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
 
92
 
 
93
        qreal offset = offsetBase;
 
94
        unsigned char cmb = glyphs[gfrom+i].attributes.combiningClass;
 
95
 
 
96
        // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
 
97
        // bits  in the glyphAttributes structure.
 
98
        if (cmb < 200) {
 
99
            // fixed position classes. We approximate by mapping to one of the others.
 
100
            // currently I added only the ones for arabic, hebrew, lao and thai.
 
101
 
 
102
            // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
 
103
 
 
104
            // add a bit more offset to arabic, a bit hacky
 
105
            if (cmb >= 27 && cmb <= 36 && offset < 3)
 
106
                offset +=1;
 
107
            // below
 
108
            if ((cmb >= 10 && cmb <= 18) ||
 
109
                 cmb == 20 || cmb == 22 ||
 
110
                 cmb == 29 || cmb == 32)
 
111
                cmb = QChar::Combining_Below;
 
112
            // above
 
113
            else if (cmb == 23 || cmb == 27 || cmb == 28 ||
 
114
                      cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
 
115
                cmb = QChar::Combining_Above;
 
116
            //below-right
 
117
            else if (cmb == 9 || cmb == 103 || cmb == 118)
 
118
                cmb = QChar::Combining_BelowRight;
 
119
            // above-right
 
120
            else if (cmb == 24 || cmb == 107 || cmb == 122)
 
121
                cmb = QChar::Combining_AboveRight;
 
122
            else if (cmb == 25)
 
123
                cmb = QChar::Combining_AboveLeft;
 
124
            // fixed:
 
125
            //  19 21
 
126
 
 
127
        }
 
128
 
 
129
        // combining marks of different class don't interact. Reset the rectangle.
 
130
        if (cmb != lastCmb) {
 
131
            //qDebug("resetting rect");
 
132
            attachmentRect = baseRect;
 
133
        }
 
134
 
 
135
        switch(cmb) {
 
136
        case QChar::Combining_DoubleBelow:
 
137
                // ### wrong in rtl context!
 
138
        case QChar::Combining_BelowLeft:
 
139
            p += QPointF(0, offset);
 
140
        case QChar::Combining_BelowLeftAttached:
 
141
            p += attachmentRect.bottomLeft() - markRect.topLeft();
 
142
            break;
 
143
        case QChar::Combining_Below:
 
144
            p += QPointF(0, offset);
 
145
        case QChar::Combining_BelowAttached:
 
146
            p += attachmentRect.bottomLeft() - markRect.topLeft();
 
147
            p += QPointF((attachmentRect.width() - markRect.width())/2 , 0);
 
148
            break;
 
149
            case QChar::Combining_BelowRight:
 
150
            p += QPointF(0, offset);
 
151
        case QChar::Combining_BelowRightAttached:
 
152
            p += attachmentRect.bottomRight() - markRect.topRight();
 
153
            break;
 
154
            case QChar::Combining_Left:
 
155
            p += QPointF(-offset, 0);
 
156
        case QChar::Combining_LeftAttached:
 
157
            break;
 
158
            case QChar::Combining_Right:
 
159
            p += QPointF(offset, 0);
 
160
        case QChar::Combining_RightAttached:
 
161
            break;
 
162
        case QChar::Combining_DoubleAbove:
 
163
            // ### wrong in RTL context!
 
164
        case QChar::Combining_AboveLeft:
 
165
            p += QPointF(0, -offset);
 
166
        case QChar::Combining_AboveLeftAttached:
 
167
            p += attachmentRect.topLeft() - markRect.bottomLeft();
 
168
            break;
 
169
        case QChar::Combining_Above:
 
170
            p += QPointF(0, -offset);
 
171
        case QChar::Combining_AboveAttached:
 
172
            p += attachmentRect.topLeft() - markRect.bottomLeft();
 
173
            p += QPointF((attachmentRect.width() - markRect.width())/2 , 0);
 
174
            break;
 
175
        case QChar::Combining_AboveRight:
 
176
            p += QPointF(0, -offset);
 
177
        case QChar::Combining_AboveRightAttached:
 
178
            p += attachmentRect.topRight() - markRect.bottomRight();
 
179
            break;
 
180
 
 
181
        case QChar::Combining_IotaSubscript:
 
182
            default:
 
183
                break;
 
184
        }
 
185
//         qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
 
186
        markRect.translate(p.x(), p.y());
 
187
        attachmentRect |= markRect;
 
188
        lastCmb = cmb;
 
189
        if (rightToLeft) {
 
190
            glyphs[gfrom+i].offset = p;
 
191
        } else {
 
192
            glyphs[gfrom+i].offset.setX(p.x() - baseInfo.xoff);
 
193
            glyphs[gfrom+i].offset.setX(p.y() - baseInfo.yoff);
 
194
        }
 
195
        glyphs[gfrom+i].advance = QPointF();
 
196
    }
 
197
}
 
198
 
 
199
 
 
200
void qt_heuristicPosition(QShaperItem *item)
 
201
{
 
202
    QGlyphLayout *glyphs = item->glyphs;
 
203
 
 
204
    int cEnd = -1;
 
205
    int i = item->num_glyphs;
 
206
    while (i--) {
 
207
        if (cEnd == -1 && glyphs[i].attributes.mark) {
 
208
            cEnd = i;
 
209
        } else if (cEnd != -1 && !glyphs[i].attributes.mark) {
 
210
            positionCluster(item, i, cEnd);
 
211
            cEnd = -1;
 
212
        }
 
213
    }
 
214
}
 
215
 
 
216
 
 
217
 
 
218
// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
 
219
// and no reordering.
 
220
// also computes logClusters heuristically
 
221
static void heuristicSetGlyphAttributes(QShaperItem *item)
 
222
{
 
223
    // ### zeroWidth and justification are missing here!!!!!
 
224
 
 
225
    Q_ASSERT(item->num_glyphs <= item->length);
 
226
 
 
227
//     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
 
228
    QGlyphLayout *glyphs = item->glyphs;
 
229
    unsigned short *logClusters = item->log_clusters;
 
230
 
 
231
    // honour the logClusters array if it exists.
 
232
    const QChar *uc = item->string->unicode() + item->from;
 
233
 
 
234
#ifndef Q_WS_MAC
 
235
    int glyph_pos = 0;
 
236
    for (int i = 0; i < item->length; i++) {
 
237
        bool surrogate = (uc[i].unicode() >= 0xd800 && uc[i].unicode() < 0xdc00 && i < item->length-1
 
238
                          && uc[i+1].unicode() >= 0xdc00 && uc[i+1].unicode() < 0xe000);
 
239
        logClusters[i] = glyph_pos;
 
240
        if (surrogate) {
 
241
            logClusters[++i] = glyph_pos;
 
242
        }
 
243
        ++glyph_pos;
 
244
    }
 
245
    Q_ASSERT(glyph_pos == item->num_glyphs);
 
246
#else
 
247
    for (int i = 0; i < item->length; ++i) {
 
248
        bool surrogate = i > 0 && (uc[i - 1].unicode() >= 0xd800 && uc[i - 1].unicode() < 0xdc00                          && uc[i].unicode() >= 0xdc00 && uc[i].unicode() < 0xe000);
 
249
        logClusters[i] = surrogate ? i - 1 : i;
 
250
 
 
251
    }
 
252
    Q_ASSERT(item->num_glyphs == item->length);
 
253
#endif
 
254
 
 
255
    // first char in a run is never (treated as) a mark
 
256
    int cStart = 0;
 
257
    glyphs[0].attributes.mark = false;
 
258
    glyphs[0].attributes.clusterStart = true;
 
259
 
 
260
    int pos = 0;
 
261
    QChar::Category lastCat = ::category(uc[0]);
 
262
    for (int i = 1; i < item->length; ++i) {
 
263
        if (logClusters[i] == pos)
 
264
            // same glyph
 
265
            continue;
 
266
        ++pos;
 
267
        while (pos < logClusters[i]) {
 
268
            glyphs[pos].attributes = glyphs[pos-1].attributes;
 
269
            ++pos;
 
270
        }
 
271
        QChar::Category cat = ::category(uc[i]);
 
272
        if (cat != QChar::Mark_NonSpacing) {
 
273
            glyphs[pos].attributes.mark = false;
 
274
            glyphs[pos].attributes.clusterStart = true;
 
275
            glyphs[pos].attributes.combiningClass = 0;
 
276
            cStart = logClusters[i];
 
277
        } else {
 
278
            int cmb = combiningClass(uc[i]);
 
279
 
 
280
            if (cmb == 0) {
 
281
                // Fix 0 combining classes
 
282
                if (uc[pos].row() == 0x0e) {
 
283
                    // thai or lao
 
284
                    unsigned char col = uc[pos].cell();
 
285
                    if (col == 0x31 ||
 
286
                         col == 0x34 ||
 
287
                         col == 0x35 ||
 
288
                         col == 0x36 ||
 
289
                         col == 0x37 ||
 
290
                         col == 0x47 ||
 
291
                         col == 0x4c ||
 
292
                         col == 0x4d ||
 
293
                         col == 0x4e) {
 
294
                        cmb = QChar::Combining_AboveRight;
 
295
                    } else if (col == 0xb1 ||
 
296
                                col == 0xb4 ||
 
297
                                col == 0xb5 ||
 
298
                                col == 0xb6 ||
 
299
                                col == 0xb7 ||
 
300
                                col == 0xbb ||
 
301
                                col == 0xcc ||
 
302
                                col == 0xcd) {
 
303
                        cmb = QChar::Combining_Above;
 
304
                    } else if (col == 0xbc) {
 
305
                        cmb = QChar::Combining_Below;
 
306
                    }
 
307
                }
 
308
            }
 
309
 
 
310
            glyphs[pos].attributes.mark = true;
 
311
            glyphs[pos].attributes.clusterStart = false;
 
312
            glyphs[pos].attributes.combiningClass = cmb;
 
313
            logClusters[i] = cStart;
 
314
            glyphs[pos].advance = QPointF();
 
315
        }
 
316
 
 
317
        if (lastCat == QChar::Separator_Space)
 
318
            glyphs[pos-1].attributes.justification = QGlyphLayout::Space;
 
319
        else if (cat != QChar::Mark_NonSpacing)
 
320
            glyphs[pos-1].attributes.justification = QGlyphLayout::Character;
 
321
        else
 
322
            glyphs[pos-1].attributes.justification = QGlyphLayout::NoJustification;
 
323
 
 
324
        lastCat = cat;
 
325
    }
 
326
    pos = logClusters[item->length-1];
 
327
    if (lastCat == QChar::Separator_Space)
 
328
        glyphs[pos].attributes.justification = QGlyphLayout::Space;
 
329
    else
 
330
        glyphs[pos].attributes.justification = QGlyphLayout::Character;
 
331
}
 
332
 
 
333
static bool basic_shape(QShaperItem *item)
 
334
{
 
335
    if (!item->font->stringToCMap(item->string->unicode()+item->from, item->length, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
 
336
        return false;
 
337
 
 
338
    heuristicSetGlyphAttributes(item);
 
339
    if (!(item->flags & QTextEngine::WidthOnly))
 
340
        qt_heuristicPosition(item);
 
341
    return true;
 
342
}
 
343
 
 
344
static void basic_attributes(int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes)
 
345
{
 
346
    const QChar *uc = text.unicode() + from;
 
347
    attributes += from;
 
348
 
 
349
    QCharAttributes *a = attributes;
 
350
 
 
351
    for (int i = 0; i < len; i++) {
 
352
        QChar::Category cat = ::category(*uc);
 
353
        a->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
 
354
        a->softBreak = false;
 
355
        a->charStop = (cat != QChar::Mark_NonSpacing);
 
356
        if (cat == QChar::Other_Surrogate) {
 
357
            // char stop only on first pair
 
358
            a->charStop = (uc->unicode() >= 0xd800 && uc->unicode() < 0xdc00 && i < len-1
 
359
                           && uc[1].unicode() >= 0xdc00 && uc[1].unicode() < 0xe000);
 
360
        }
 
361
        a->wordStop = (*uc == QChar::LineSeparator);
 
362
        a->invalid = false;
 
363
        ++uc;
 
364
        ++a;
 
365
    }
 
366
}
 
367
 
 
368
#if defined(Q_WS_QWS)
 
369
static bool unicode_shape(QShaperItem *item)
 
370
{
 
371
    QString s = item->string->mid(item->from, item->length);
 
372
    QChar *c = (QChar *)s.unicode();
 
373
    for (int i = 0; i < item->length; ++i) {
 
374
        ushort uc = c[i].unicode();
 
375
        if (uc >= 0x200e && uc < 0x2070) {
 
376
            if (uc < 0x2010
 
377
                || (uc >= 0x2028 && uc <= 0x202f)
 
378
                || uc >= 0x206a)
 
379
                c[i] = QChar(0x20);
 
380
        }
 
381
    }
 
382
    if (!item->font->stringToCMap(s.unicode(), item->length, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
 
383
        return false;
 
384
    const QChar *cc = item->string->unicode() + item->from;
 
385
    for (int i = 0; i < item->length; ++i) {
 
386
        ushort uc = cc[i].unicode();
 
387
        if (uc >= 0x200e && uc < 0x2070) {
 
388
            if (uc < 0x2010
 
389
                || (uc >= 0x2028 && uc <= 0x202f)
 
390
                || uc >= 0x206a)
 
391
                item->glyphs[i].advance = QPointF();
 
392
        }
 
393
    }
 
394
 
 
395
    heuristicSetGlyphAttributes(item);
 
396
    if (!(item->flags & QTextEngine::WidthOnly))
 
397
        qt_heuristicPosition(item);
 
398
    return true;
 
399
}
 
400
#endif
 
401
 
 
402
 
 
403
// --------------------------------------------------------------------------------------------------------------------------------------------
 
404
//
 
405
// Middle eastern languages
 
406
//
 
407
// --------------------------------------------------------------------------------------------------------------------------------------------
 
408
 
 
409
 
 
410
// these groups correspond to the groups defined in the Unicode standard.
 
411
// Some of these groups are equal whith regards to both joining and line breaking behaviour,
 
412
// and thus have the same enum value
 
413
//
 
414
// I'm not sure the mapping of syriac to arabic enums is correct with regards to justification, but as
 
415
// I couldn't find any better document I'll hope for the best.
 
416
enum ArabicGroup {
 
417
    // NonJoining
 
418
    ArabicNone,
 
419
    ArabicSpace,
 
420
    // Transparent
 
421
    Transparent,
 
422
    // Causing
 
423
    Center,
 
424
    Kashida,
 
425
 
 
426
    // Arabic
 
427
    // Dual
 
428
    Beh,
 
429
    Noon,
 
430
    Meem = Noon,
 
431
    Heh = Noon,
 
432
    KnottedHeh = Noon,
 
433
    HehGoal = Noon,
 
434
    SwashKaf = Noon,
 
435
    Yeh,
 
436
    Hah,
 
437
    Seen,
 
438
    Sad = Seen,
 
439
    Tah,
 
440
    Kaf = Tah,
 
441
    Gaf = Tah,
 
442
    Lam = Tah,
 
443
    Ain,
 
444
    Feh = Ain,
 
445
    Qaf = Ain,
 
446
    // Right
 
447
    Alef,
 
448
    Waw,
 
449
    Dal,
 
450
    TehMarbuta = Dal,
 
451
    Reh,
 
452
    HamzaOnHehGoal,
 
453
    YehWithTail = HamzaOnHehGoal,
 
454
    YehBarre = HamzaOnHehGoal,
 
455
 
 
456
    // Syriac
 
457
    // Dual
 
458
    Beth = Beh,
 
459
    Gamal = Ain,
 
460
    Heth = Noon,
 
461
    Teth = Hah,
 
462
    Yudh = Noon,
 
463
    Kaph = Noon,
 
464
    Lamadh = Lam,
 
465
    Mim = Noon,
 
466
    Nun = Noon,
 
467
    Semakh = Noon,
 
468
    FinalSemakh = Noon,
 
469
    SyriacE = Ain,
 
470
    Pe = Ain,
 
471
    ReversedPe = Hah,
 
472
    Qaph = Noon,
 
473
    Shin = Noon,
 
474
    Fe = Ain,
 
475
 
 
476
    // Right
 
477
    Alaph = Alef,
 
478
    Dalath = Dal,
 
479
    He = Dal,
 
480
    SyriacWaw = Waw,
 
481
    Zain = Alef,
 
482
    YudhHe = Waw,
 
483
    Sadhe = HamzaOnHehGoal,
 
484
    Taw = Dal,
 
485
 
 
486
    // Compiler bug? Otherwise ArabicGroupsEnd would be equal to Dal + 1.
 
487
    Dummy = HamzaOnHehGoal,
 
488
    ArabicGroupsEnd
 
489
};
 
490
 
 
491
static const unsigned char arabic_group[0x150] = {
 
492
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
493
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
494
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
495
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
496
 
 
497
    Transparent, Transparent, Transparent, Transparent,
 
498
    Transparent, Transparent, ArabicNone, ArabicNone,
 
499
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
500
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
501
 
 
502
    ArabicNone, ArabicNone, Alef, Alef,
 
503
    Waw, Alef, Yeh, Alef,
 
504
    Beh, TehMarbuta, Beh, Beh,
 
505
    Hah, Hah, Hah, Dal,
 
506
 
 
507
    Dal, Reh, Reh, Seen,
 
508
    Seen, Sad, Sad, Tah,
 
509
    Tah, Ain, Ain, ArabicNone,
 
510
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
511
 
 
512
    // 0x640
 
513
    Kashida, Feh, Qaf, Kaf,
 
514
    Lam, Meem, Noon, Heh,
 
515
    Waw, Yeh, Yeh, Transparent,
 
516
    Transparent, Transparent, Transparent, Transparent,
 
517
 
 
518
    Transparent, Transparent, Transparent, Transparent,
 
519
    Transparent, Transparent, Transparent, Transparent,
 
520
    Transparent, ArabicNone, ArabicNone, ArabicNone,
 
521
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
522
 
 
523
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
524
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
525
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
526
    ArabicNone, ArabicNone, Beh, Qaf,
 
527
 
 
528
    Transparent, Alef, Alef, Alef,
 
529
    ArabicNone, Alef, Waw, Waw,
 
530
    Yeh, Beh, Beh, Beh,
 
531
    Beh, Beh, Beh, Beh,
 
532
 
 
533
    // 0x680
 
534
    Beh, Hah, Hah, Hah,
 
535
    Hah, Hah, Hah, Hah,
 
536
    Dal, Dal, Dal, Dal,
 
537
    Dal, Dal, Dal, Dal,
 
538
 
 
539
    Dal, Reh, Reh, Reh,
 
540
    Reh, Reh, Reh, Reh,
 
541
    Reh, Reh, Seen, Seen,
 
542
    Seen, Sad, Sad, Tah,
 
543
 
 
544
    Ain, Feh, Feh, Feh,
 
545
    Feh, Feh, Feh, Qaf,
 
546
    Qaf, Gaf, SwashKaf, Gaf,
 
547
    Kaf, Kaf, Kaf, Gaf,
 
548
 
 
549
    Gaf, Gaf, Gaf, Gaf,
 
550
    Gaf, Lam, Lam, Lam,
 
551
    Lam, Noon, Noon, Noon,
 
552
    Noon, Noon, KnottedHeh, Hah,
 
553
 
 
554
    // 0x6c0
 
555
    TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
 
556
    Waw, Waw, Waw, Waw,
 
557
    Waw, Waw, Waw, Waw,
 
558
    Yeh, YehWithTail, Yeh, Waw,
 
559
 
 
560
    Yeh, Yeh, YehBarre, YehBarre,
 
561
    ArabicNone, TehMarbuta, Transparent, Transparent,
 
562
    Transparent, Transparent, Transparent, Transparent,
 
563
    Transparent, ArabicNone, ArabicNone, Transparent,
 
564
 
 
565
    Transparent, Transparent, Transparent, Transparent,
 
566
    Transparent, ArabicNone, ArabicNone, Transparent,
 
567
    Transparent, ArabicNone, Transparent, Transparent,
 
568
    Transparent, Transparent, Dal, Reh,
 
569
 
 
570
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
571
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
572
    ArabicNone, ArabicNone, Seen, Sad,
 
573
    Ain, ArabicNone, ArabicNone, KnottedHeh,
 
574
 
 
575
    // 0x700
 
576
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
577
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
578
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
579
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
 
580
 
 
581
    Alaph, Transparent, Beth, Gamal,
 
582
    Gamal, Dalath, Dalath, He,
 
583
    SyriacWaw, Zain, Heth, Teth,
 
584
    Teth, Yudh, YudhHe, Kaph,
 
585
 
 
586
    Lamadh, Mim, Nun, Semakh,
 
587
    FinalSemakh, SyriacE, Pe, ReversedPe,
 
588
    Sadhe, Qaph, Dalath, Shin,
 
589
    Taw, Beth, Gamal, Dalath,
 
590
 
 
591
    Transparent, Transparent, Transparent, Transparent,
 
592
    Transparent, Transparent, Transparent, Transparent,
 
593
    Transparent, Transparent, Transparent, Transparent,
 
594
    Transparent, Transparent, Transparent, Transparent,
 
595
 
 
596
    Transparent, Transparent, Transparent, Transparent,
 
597
    Transparent, Transparent, Transparent, Transparent,
 
598
    Transparent, Transparent, Transparent, ArabicNone,
 
599
    ArabicNone, Zain, Kaph, Fe,
 
600
};
 
601
 
 
602
static inline ArabicGroup arabicGroup(unsigned short uc)
 
603
{
 
604
    if (uc >= 0x0600 && uc < 0x750)
 
605
        return (ArabicGroup) arabic_group[uc-0x600];
 
606
    else if (uc == 0x200d)
 
607
        return Center;
 
608
    else if (::category(uc) == QChar::Separator_Space)
 
609
        return ArabicSpace;
 
610
    else
 
611
        return ArabicNone;
 
612
}
 
613
 
 
614
 
 
615
/*
 
616
   Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
 
617
   arabic).
 
618
 
 
619
   Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
 
620
   transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
 
621
 
 
622
   Right join-causing: dual + center
 
623
   Left join-causing: dual + right + center
 
624
 
 
625
   Rules are as follows (for a string already in visual order, as we have it here):
 
626
 
 
627
   R1 Transparent characters do not affect joining behaviour.
 
628
   R2 A right joining character, that has a right join-causing char on the right will get form XRight
 
629
   (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
 
630
   Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
 
631
   R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
 
632
             the right will get form XMedial
 
633
   R5  A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
 
634
         will get form XRight
 
635
   R6 A dual joining character, that has a  left join causing char on the left, and no right join causing char on the right
 
636
         will get form XLeft
 
637
   R7 Otherwise the character will get form XIsolated
 
638
 
 
639
   Additionally we have to do the minimal ligature support for lam-alef ligatures:
 
640
 
 
641
   L1 Transparent characters do not affect ligature behaviour.
 
642
   L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
 
643
   L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
 
644
 
 
645
   The state table below handles rules R1-R7.
 
646
*/
 
647
 
 
648
enum Shape {
 
649
    XIsolated,
 
650
    XFinal,
 
651
    XInitial,
 
652
    XMedial,
 
653
    // intermediate state
 
654
    XCausing
 
655
};
 
656
 
 
657
 
 
658
enum Joining {
 
659
    JNone,
 
660
    JCausing,
 
661
    JDual,
 
662
    JRight,
 
663
    JTransparent
 
664
};
 
665
 
 
666
 
 
667
static const Joining joining_for_group[ArabicGroupsEnd] = {
 
668
    // NonJoining
 
669
    JNone, // ArabicNone
 
670
    JNone, // ArabicSpace
 
671
    // Transparent
 
672
    JTransparent, // Transparent
 
673
    // Causing
 
674
    JCausing, // Center
 
675
    JCausing, // Kashida
 
676
    // Dual
 
677
    JDual, // Beh
 
678
    JDual, // Noon
 
679
    JDual, // Yeh
 
680
    JDual, // Hah
 
681
    JDual, // Seen
 
682
    JDual, // Tah
 
683
    JDual, // Ain
 
684
    // Right
 
685
    JRight, // Alef
 
686
    JRight, // Waw
 
687
    JRight, // Dal
 
688
    JRight, // Reh
 
689
    JRight  // HamzaOnHehGoal
 
690
};
 
691
 
 
692
 
 
693
struct JoiningPair {
 
694
    Shape form1;
 
695
    Shape form2;
 
696
};
 
697
 
 
698
static const JoiningPair joining_table[5][4] =
 
699
// None, Causing, Dual, Right
 
700
{
 
701
    { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } }, // XIsolated
 
702
    { { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } }, // XFinal
 
703
    { { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } }, // XInitial
 
704
    { { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } }, // XMedial
 
705
    { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } }, // XCausing
 
706
};
 
707
 
 
708
 
 
709
/*
 
710
According to http://www.microsoft.com/middleeast/Arabicdev/IE6/KBase.asp
 
711
 
 
712
1. Find the priority of the connecting opportunities in each word
 
713
2. Add expansion at the highest priority connection opportunity
 
714
3. If more than one connection opportunity have the same highest value,
 
715
   use the opportunity closest to the end of the word.
 
716
 
 
717
Following is a chart that provides the priority for connection
 
718
opportunities and where expansion occurs. The character group names
 
719
are those in table 6.6 of the UNICODE 2.0 book.
 
720
 
 
721
 
 
722
PrioritY        Glyph                   Condition                                       Kashida Location
 
723
 
 
724
Arabic_Kashida        User inserted Kashida   The user entered a Kashida in a position.       After the user
 
725
                (Shift+j or Shift+ļæ½)    Thus, it is the highest priority to insert an   inserted kashida
 
726
                                        automatic kashida.
 
727
 
 
728
Arabic_Seen        Seen, Sad               Connecting to the next character.               After the character.
 
729
                                        (Initial or medial form).
 
730
 
 
731
Arabic_HaaDal        Teh Marbutah, Haa, Dal  Connecting to previous character.               Before the final form
 
732
                                                                                        of these characters.
 
733
 
 
734
Arabic_Alef     Alef, Tah, Lam,         Connecting to previous character.               Before the final form
 
735
                Kaf and Gaf                                                             of these characters.
 
736
 
 
737
Arabic_BaRa     Reh, Yeh                Connected to medial Beh                         Before preceding medial Baa
 
738
 
 
739
Arabic_Waw        Waw, Ain, Qaf, Feh      Connecting to previous character.               Before the final form of
 
740
                                                                                        these characters.
 
741
 
 
742
Arabic_Normal   Other connecting        Connecting to previous character.               Before the final form
 
743
                characters                                                              of these characters.
 
744
 
 
745
 
 
746
 
 
747
This seems to imply that we have at most one kashida point per arabic word.
 
748
 
 
749
*/
 
750
 
 
751
struct ArabicProperties {
 
752
    unsigned char shape;
 
753
    unsigned char justification;
 
754
};
 
755
Q_DECLARE_TYPEINFO(ArabicProperties, Q_PRIMITIVE_TYPE);
 
756
 
 
757
 
 
758
static void getArabicProperties(const unsigned short *chars, int len, ArabicProperties *properties)
 
759
{
 
760
//     qDebug("arabicSyriacOpenTypeShape: properties:");
 
761
    int lastPos = 0;
 
762
    int lastGroup = ArabicNone;
 
763
 
 
764
    ArabicGroup group = arabicGroup(chars[0]);
 
765
    Joining j = joining_for_group[group];
 
766
    Shape shape = joining_table[XIsolated][j].form2;
 
767
    properties[0].justification = QGlyphLayout::NoJustification;
 
768
 
 
769
    for (int i = 1; i < len; ++i) {
 
770
        // #### fix handling for spaces and punktuation
 
771
        properties[i].justification = QGlyphLayout::NoJustification;
 
772
 
 
773
        group = arabicGroup(chars[i]);
 
774
        j = joining_for_group[group];
 
775
 
 
776
        if (j == JTransparent) {
 
777
            properties[i].shape = XIsolated;
 
778
            continue;
 
779
        }
 
780
 
 
781
        properties[lastPos].shape = joining_table[shape][j].form1;
 
782
        shape = joining_table[shape][j].form2;
 
783
 
 
784
        switch(lastGroup) {
 
785
        case Seen:
 
786
            if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
 
787
                properties[i-1].justification = QGlyphLayout::Arabic_Seen;
 
788
            break;
 
789
        case Hah:
 
790
            if (properties[lastPos].shape == XFinal)
 
791
                properties[lastPos-1].justification = QGlyphLayout::Arabic_HaaDal;
 
792
            break;
 
793
        case Alef:
 
794
            if (properties[lastPos].shape == XFinal)
 
795
                properties[lastPos-1].justification = QGlyphLayout::Arabic_Alef;
 
796
            break;
 
797
        case Ain:
 
798
            if (properties[lastPos].shape == XFinal)
 
799
                properties[lastPos-1].justification = QGlyphLayout::Arabic_Waw;
 
800
            break;
 
801
        case Noon:
 
802
            if (properties[lastPos].shape == XFinal)
 
803
                properties[lastPos-1].justification = QGlyphLayout::Arabic_Normal;
 
804
            break;
 
805
        case ArabicNone:
 
806
            break;
 
807
 
 
808
        default:
 
809
            Q_ASSERT(false);
 
810
        }
 
811
 
 
812
        lastGroup = ArabicNone;
 
813
 
 
814
        switch(group) {
 
815
        case ArabicNone:
 
816
        case Transparent:
 
817
        // ### Center should probably be treated as transparent when it comes to justification.
 
818
        case Center:
 
819
            break;
 
820
        case ArabicSpace:
 
821
            properties[i].justification = QGlyphLayout::Arabic_Space;
 
822
 
 
823
        case Kashida:
 
824
            properties[i].justification = QGlyphLayout::Arabic_Kashida;
 
825
            break;
 
826
        case Seen:
 
827
            lastGroup = Seen;
 
828
            break;
 
829
 
 
830
        case Hah:
 
831
        case Dal:
 
832
            lastGroup = Hah;
 
833
            break;
 
834
 
 
835
        case Alef:
 
836
        case Tah:
 
837
            lastGroup = Alef;
 
838
            break;
 
839
 
 
840
        case Yeh:
 
841
        case Reh:
 
842
            if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
 
843
                properties[lastPos-1].justification = QGlyphLayout::Arabic_BaRa;
 
844
            break;
 
845
 
 
846
        case Ain:
 
847
        case Waw:
 
848
            lastGroup = Ain;
 
849
            break;
 
850
 
 
851
        case Noon:
 
852
        case Beh:
 
853
        case HamzaOnHehGoal:
 
854
            lastGroup = Noon;
 
855
            break;
 
856
        case ArabicGroupsEnd:
 
857
            Q_ASSERT(false);
 
858
        }
 
859
 
 
860
        lastPos = i;
 
861
    }
 
862
    properties[lastPos].shape = joining_table[shape][JNone].form1;
 
863
 
 
864
 
 
865
//     for (int i = 0; i < len; ++i)
 
866
//         qDebug("arabic properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
 
867
}
 
868
 
 
869
 
 
870
 
 
871
 
 
872
 
 
873
 
 
874
// The unicode to unicode shaping codec.
 
875
// does only presentation forms B at the moment, but that should be enough for
 
876
// simple display
 
877
static const ushort arabicUnicodeMapping[256][2] = {
 
878
    // base of shaped forms, and number-1 of them (0 for non shaping,
 
879
    // 1 for right binding and 3 for dual binding
 
880
 
 
881
    // These are just the glyphs available in Unicode,
 
882
    // some characters are in R class, but have no glyphs in Unicode.
 
883
 
 
884
    { 0x0600, 0 }, // 0x0600
 
885
    { 0x0601, 0 }, // 0x0601
 
886
    { 0x0602, 0 }, // 0x0602
 
887
    { 0x0603, 0 }, // 0x0603
 
888
    { 0x0604, 0 }, // 0x0604
 
889
    { 0x0605, 0 }, // 0x0605
 
890
    { 0x0606, 0 }, // 0x0606
 
891
    { 0x0607, 0 }, // 0x0607
 
892
    { 0x0608, 0 }, // 0x0608
 
893
    { 0x0609, 0 }, // 0x0609
 
894
    { 0x060A, 0 }, // 0x060A
 
895
    { 0x060B, 0 }, // 0x060B
 
896
    { 0x060C, 0 }, // 0x060C
 
897
    { 0x060D, 0 }, // 0x060D
 
898
    { 0x060E, 0 }, // 0x060E
 
899
    { 0x060F, 0 }, // 0x060F
 
900
 
 
901
    { 0x0610, 0 }, // 0x0610
 
902
    { 0x0611, 0 }, // 0x0611
 
903
    { 0x0612, 0 }, // 0x0612
 
904
    { 0x0613, 0 }, // 0x0613
 
905
    { 0x0614, 0 }, // 0x0614
 
906
    { 0x0615, 0 }, // 0x0615
 
907
    { 0x0616, 0 }, // 0x0616
 
908
    { 0x0617, 0 }, // 0x0617
 
909
    { 0x0618, 0 }, // 0x0618
 
910
    { 0x0619, 0 }, // 0x0619
 
911
    { 0x061A, 0 }, // 0x061A
 
912
    { 0x061B, 0 }, // 0x061B
 
913
    { 0x061C, 0 }, // 0x061C
 
914
    { 0x061D, 0 }, // 0x061D
 
915
    { 0x061E, 0 }, // 0x061E
 
916
    { 0x061F, 0 }, // 0x061F
 
917
 
 
918
    { 0x0620, 0 }, // 0x0620
 
919
    { 0xFE80, 0 }, // 0x0621            HAMZA
 
920
    { 0xFE81, 1 }, // 0x0622    R       ALEF WITH MADDA ABOVE
 
921
    { 0xFE83, 1 }, // 0x0623    R       ALEF WITH HAMZA ABOVE
 
922
    { 0xFE85, 1 }, // 0x0624    R       WAW WITH HAMZA ABOVE
 
923
    { 0xFE87, 1 }, // 0x0625    R       ALEF WITH HAMZA BELOW
 
924
    { 0xFE89, 3 }, // 0x0626    D       YEH WITH HAMZA ABOVE
 
925
    { 0xFE8D, 1 }, // 0x0627    R       ALEF
 
926
    { 0xFE8F, 3 }, // 0x0628    D       BEH
 
927
    { 0xFE93, 1 }, // 0x0629    R       TEH MARBUTA
 
928
    { 0xFE95, 3 }, // 0x062A    D       TEH
 
929
    { 0xFE99, 3 }, // 0x062B    D       THEH
 
930
    { 0xFE9D, 3 }, // 0x062C    D       JEEM
 
931
    { 0xFEA1, 3 }, // 0x062D    D       HAH
 
932
    { 0xFEA5, 3 }, // 0x062E    D       KHAH
 
933
    { 0xFEA9, 1 }, // 0x062F    R       DAL
 
934
 
 
935
    { 0xFEAB, 1 }, // 0x0630    R       THAL
 
936
    { 0xFEAD, 1 }, // 0x0631    R       REH
 
937
    { 0xFEAF, 1 }, // 0x0632    R       ZAIN
 
938
    { 0xFEB1, 3 }, // 0x0633    D       SEEN
 
939
    { 0xFEB5, 3 }, // 0x0634    D       SHEEN
 
940
    { 0xFEB9, 3 }, // 0x0635    D       SAD
 
941
    { 0xFEBD, 3 }, // 0x0636    D       DAD
 
942
    { 0xFEC1, 3 }, // 0x0637    D       TAH
 
943
    { 0xFEC5, 3 }, // 0x0638    D       ZAH
 
944
    { 0xFEC9, 3 }, // 0x0639    D       AIN
 
945
    { 0xFECD, 3 }, // 0x063A    D       GHAIN
 
946
    { 0x063B, 0 }, // 0x063B
 
947
    { 0x063C, 0 }, // 0x063C
 
948
    { 0x063D, 0 }, // 0x063D
 
949
    { 0x063E, 0 }, // 0x063E
 
950
    { 0x063F, 0 }, // 0x063F
 
951
 
 
952
    { 0x0640, 0 }, // 0x0640    C       TATWEEL // ### Join Causing, only one glyph
 
953
    { 0xFED1, 3 }, // 0x0641    D       FEH
 
954
    { 0xFED5, 3 }, // 0x0642    D       QAF
 
955
    { 0xFED9, 3 }, // 0x0643    D       KAF
 
956
    { 0xFEDD, 3 }, // 0x0644    D       LAM
 
957
    { 0xFEE1, 3 }, // 0x0645    D       MEEM
 
958
    { 0xFEE5, 3 }, // 0x0646    D       NOON
 
959
    { 0xFEE9, 3 }, // 0x0647    D       HEH
 
960
    { 0xFEED, 1 }, // 0x0648    R       WAW
 
961
    { 0x0649, 3 }, // 0x0649            ALEF MAKSURA // ### Dual, glyphs not consecutive, handle in code.
 
962
    { 0xFEF1, 3 }, // 0x064A    D       YEH
 
963
    { 0x064B, 0 }, // 0x064B
 
964
    { 0x064C, 0 }, // 0x064C
 
965
    { 0x064D, 0 }, // 0x064D
 
966
    { 0x064E, 0 }, // 0x064E
 
967
    { 0x064F, 0 }, // 0x064F
 
968
 
 
969
    { 0x0650, 0 }, // 0x0650
 
970
    { 0x0651, 0 }, // 0x0651
 
971
    { 0x0652, 0 }, // 0x0652
 
972
    { 0x0653, 0 }, // 0x0653
 
973
    { 0x0654, 0 }, // 0x0654
 
974
    { 0x0655, 0 }, // 0x0655
 
975
    { 0x0656, 0 }, // 0x0656
 
976
    { 0x0657, 0 }, // 0x0657
 
977
    { 0x0658, 0 }, // 0x0658
 
978
    { 0x0659, 0 }, // 0x0659
 
979
    { 0x065A, 0 }, // 0x065A
 
980
    { 0x065B, 0 }, // 0x065B
 
981
    { 0x065C, 0 }, // 0x065C
 
982
    { 0x065D, 0 }, // 0x065D
 
983
    { 0x065E, 0 }, // 0x065E
 
984
    { 0x065F, 0 }, // 0x065F
 
985
 
 
986
    { 0x0660, 0 }, // 0x0660
 
987
    { 0x0661, 0 }, // 0x0661
 
988
    { 0x0662, 0 }, // 0x0662
 
989
    { 0x0663, 0 }, // 0x0663
 
990
    { 0x0664, 0 }, // 0x0664
 
991
    { 0x0665, 0 }, // 0x0665
 
992
    { 0x0666, 0 }, // 0x0666
 
993
    { 0x0667, 0 }, // 0x0667
 
994
    { 0x0668, 0 }, // 0x0668
 
995
    { 0x0669, 0 }, // 0x0669
 
996
    { 0x066A, 0 }, // 0x066A
 
997
    { 0x066B, 0 }, // 0x066B
 
998
    { 0x066C, 0 }, // 0x066C
 
999
    { 0x066D, 0 }, // 0x066D
 
1000
    { 0x066E, 0 }, // 0x066E
 
1001
    { 0x066F, 0 }, // 0x066F
 
1002
 
 
1003
    { 0x0670, 0 }, // 0x0670
 
1004
    { 0xFB50, 1 }, // 0x0671    R       ALEF WASLA
 
1005
    { 0x0672, 0 }, // 0x0672
 
1006
    { 0x0673, 0 }, // 0x0673
 
1007
    { 0x0674, 0 }, // 0x0674
 
1008
    { 0x0675, 0 }, // 0x0675
 
1009
    { 0x0676, 0 }, // 0x0676
 
1010
    { 0x0677, 0 }, // 0x0677
 
1011
    { 0x0678, 0 }, // 0x0678
 
1012
    { 0xFB66, 3 }, // 0x0679    D       TTEH
 
1013
    { 0xFB5E, 3 }, // 0x067A    D       TTEHEH
 
1014
    { 0xFB52, 3 }, // 0x067B    D       BEEH
 
1015
    { 0x067C, 0 }, // 0x067C
 
1016
    { 0x067D, 0 }, // 0x067D
 
1017
    { 0xFB56, 3 }, // 0x067E    D       PEH
 
1018
    { 0xFB62, 3 }, // 0x067F    D       TEHEH
 
1019
 
 
1020
    { 0xFB5A, 3 }, // 0x0680    D       BEHEH
 
1021
    { 0x0681, 0 }, // 0x0681
 
1022
    { 0x0682, 0 }, // 0x0682
 
1023
    { 0xFB76, 3 }, // 0x0683    D       NYEH
 
1024
    { 0xFB72, 3 }, // 0x0684    D       DYEH
 
1025
    { 0x0685, 0 }, // 0x0685
 
1026
    { 0xFB7A, 3 }, // 0x0686    D       TCHEH
 
1027
    { 0xFB7E, 3 }, // 0x0687    D       TCHEHEH
 
1028
    { 0xFB88, 1 }, // 0x0688    R       DDAL
 
1029
    { 0x0689, 0 }, // 0x0689
 
1030
    { 0x068A, 0 }, // 0x068A
 
1031
    { 0x068B, 0 }, // 0x068B
 
1032
    { 0xFB84, 1 }, // 0x068C    R       DAHAL
 
1033
    { 0xFB82, 1 }, // 0x068D    R       DDAHAL
 
1034
    { 0xFB86, 1 }, // 0x068E    R       DUL
 
1035
    { 0x068F, 0 }, // 0x068F
 
1036
 
 
1037
    { 0x0690, 0 }, // 0x0690
 
1038
    { 0xFB8C, 1 }, // 0x0691    R       RREH
 
1039
    { 0x0692, 0 }, // 0x0692
 
1040
    { 0x0693, 0 }, // 0x0693
 
1041
    { 0x0694, 0 }, // 0x0694
 
1042
    { 0x0695, 0 }, // 0x0695
 
1043
    { 0x0696, 0 }, // 0x0696
 
1044
    { 0x0697, 0 }, // 0x0697
 
1045
    { 0xFB8A, 1 }, // 0x0698    R       JEH
 
1046
    { 0x0699, 0 }, // 0x0699
 
1047
    { 0x069A, 0 }, // 0x069A
 
1048
    { 0x069B, 0 }, // 0x069B
 
1049
    { 0x069C, 0 }, // 0x069C
 
1050
    { 0x069D, 0 }, // 0x069D
 
1051
    { 0x069E, 0 }, // 0x069E
 
1052
    { 0x069F, 0 }, // 0x069F
 
1053
 
 
1054
    { 0x06A0, 0 }, // 0x06A0
 
1055
    { 0x06A1, 0 }, // 0x06A1
 
1056
    { 0x06A2, 0 }, // 0x06A2
 
1057
    { 0x06A3, 0 }, // 0x06A3
 
1058
    { 0xFB6A, 3 }, // 0x06A4    D       VEH
 
1059
    { 0x06A5, 0 }, // 0x06A5
 
1060
    { 0xFB6E, 3 }, // 0x06A6    D       PEHEH
 
1061
    { 0x06A7, 0 }, // 0x06A7
 
1062
    { 0x06A8, 0 }, // 0x06A8
 
1063
    { 0xFB8E, 3 }, // 0x06A9    D       KEHEH
 
1064
    { 0x06AA, 0 }, // 0x06AA
 
1065
    { 0x06AB, 0 }, // 0x06AB
 
1066
    { 0x06AC, 0 }, // 0x06AC
 
1067
    { 0xFBD3, 3 }, // 0x06AD    D       NG
 
1068
    { 0x06AE, 0 }, // 0x06AE
 
1069
    { 0xFB92, 3 }, // 0x06AF    D       GAF
 
1070
 
 
1071
    { 0x06B0, 0 }, // 0x06B0
 
1072
    { 0xFB9A, 3 }, // 0x06B1    D       NGOEH
 
1073
    { 0x06B2, 0 }, // 0x06B2
 
1074
    { 0xFB96, 3 }, // 0x06B3    D       GUEH
 
1075
    { 0x06B4, 0 }, // 0x06B4
 
1076
    { 0x06B5, 0 }, // 0x06B5
 
1077
    { 0x06B6, 0 }, // 0x06B6
 
1078
    { 0x06B7, 0 }, // 0x06B7
 
1079
    { 0x06B8, 0 }, // 0x06B8
 
1080
    { 0x06B9, 0 }, // 0x06B9
 
1081
    { 0xFB9E, 1 }, // 0x06BA    R       NOON GHUNNA
 
1082
    { 0xFBA0, 3 }, // 0x06BB    D       RNOON
 
1083
    { 0x06BC, 0 }, // 0x06BC
 
1084
    { 0x06BD, 0 }, // 0x06BD
 
1085
    { 0xFBAA, 3 }, // 0x06BE    D       HEH DOACHASHMEE
 
1086
    { 0x06BF, 0 }, // 0x06BF
 
1087
 
 
1088
    { 0xFBA4, 1 }, // 0x06C0    R       HEH WITH YEH ABOVE
 
1089
    { 0xFBA6, 3 }, // 0x06C1    D       HEH GOAL
 
1090
    { 0x06C2, 0 }, // 0x06C2
 
1091
    { 0x06C3, 0 }, // 0x06C3
 
1092
    { 0x06C4, 0 }, // 0x06C4
 
1093
    { 0xFBE0, 1 }, // 0x06C5    R       KIRGHIZ OE
 
1094
    { 0xFBD9, 1 }, // 0x06C6    R       OE
 
1095
    { 0xFBD7, 1 }, // 0x06C7    R       U
 
1096
    { 0xFBDB, 1 }, // 0x06C8    R       YU
 
1097
    { 0xFBE2, 1 }, // 0x06C9    R       KIRGHIZ YU
 
1098
    { 0x06CA, 0 }, // 0x06CA
 
1099
    { 0xFBDE, 1 }, // 0x06CB    R       VE
 
1100
    { 0xFBFC, 3 }, // 0x06CC    D       FARSI YEH
 
1101
    { 0x06CD, 0 }, // 0x06CD
 
1102
    { 0x06CE, 0 }, // 0x06CE
 
1103
    { 0x06CF, 0 }, // 0x06CF
 
1104
 
 
1105
    { 0xFBE4, 3 }, // 0x06D0    D       E
 
1106
    { 0x06D1, 0 }, // 0x06D1
 
1107
    { 0xFBAE, 1 }, // 0x06D2    R       YEH BARREE
 
1108
    { 0xFBB0, 1 }, // 0x06D3    R       YEH BARREE WITH HAMZA ABOVE
 
1109
    { 0x06D4, 0 }, // 0x06D4
 
1110
    { 0x06D5, 0 }, // 0x06D5
 
1111
    { 0x06D6, 0 }, // 0x06D6
 
1112
    { 0x06D7, 0 }, // 0x06D7
 
1113
    { 0x06D8, 0 }, // 0x06D8
 
1114
    { 0x06D9, 0 }, // 0x06D9
 
1115
    { 0x06DA, 0 }, // 0x06DA
 
1116
    { 0x06DB, 0 }, // 0x06DB
 
1117
    { 0x06DC, 0 }, // 0x06DC
 
1118
    { 0x06DD, 0 }, // 0x06DD
 
1119
    { 0x06DE, 0 }, // 0x06DE
 
1120
    { 0x06DF, 0 }, // 0x06DF
 
1121
 
 
1122
    { 0x06E0, 0 }, // 0x06E0
 
1123
    { 0x06E1, 0 }, // 0x06E1
 
1124
    { 0x06E2, 0 }, // 0x06E2
 
1125
    { 0x06E3, 0 }, // 0x06E3
 
1126
    { 0x06E4, 0 }, // 0x06E4
 
1127
    { 0x06E5, 0 }, // 0x06E5
 
1128
    { 0x06E6, 0 }, // 0x06E6
 
1129
    { 0x06E7, 0 }, // 0x06E7
 
1130
    { 0x06E8, 0 }, // 0x06E8
 
1131
    { 0x06E9, 0 }, // 0x06E9
 
1132
    { 0x06EA, 0 }, // 0x06EA
 
1133
    { 0x06EB, 0 }, // 0x06EB
 
1134
    { 0x06EC, 0 }, // 0x06EC
 
1135
    { 0x06ED, 0 }, // 0x06ED
 
1136
    { 0x06EE, 0 }, // 0x06EE
 
1137
    { 0x06EF, 0 }, // 0x06EF
 
1138
 
 
1139
    { 0x06F0, 0 }, // 0x06F0
 
1140
    { 0x06F1, 0 }, // 0x06F1
 
1141
    { 0x06F2, 0 }, // 0x06F2
 
1142
    { 0x06F3, 0 }, // 0x06F3
 
1143
    { 0x06F4, 0 }, // 0x06F4
 
1144
    { 0x06F5, 0 }, // 0x06F5
 
1145
    { 0x06F6, 0 }, // 0x06F6
 
1146
    { 0x06F7, 0 }, // 0x06F7
 
1147
    { 0x06F8, 0 }, // 0x06F8
 
1148
    { 0x06F9, 0 }, // 0x06F9
 
1149
    { 0x06FA, 0 }, // 0x06FA
 
1150
    { 0x06FB, 0 }, // 0x06FB
 
1151
    { 0x06FC, 0 }, // 0x06FC
 
1152
    { 0x06FD, 0 }, // 0x06FD
 
1153
    { 0x06FE, 0 }, // 0x06FE
 
1154
    { 0x06FF, 0 }  // 0x06FF
 
1155
};
 
1156
 
 
1157
// the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, this table does
 
1158
static const ushort alefMaksura[4] = {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9};
 
1159
 
 
1160
// this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
 
1161
// of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
 
1162
// medial to the final form
 
1163
static const ushort arabicUnicodeLamAlefMapping[6][4] = {
 
1164
    { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, // 0x622        R       Alef with Madda above
 
1165
    { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, // 0x623        R       Alef with Hamza above
 
1166
    { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x624        // Just to fill the table ;-)
 
1167
    { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, // 0x625        R       Alef with Hamza below
 
1168
    { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x626        // Just to fill the table ;-)
 
1169
    { 0xfffd, 0xfffd, 0xfefb, 0xfefc }  // 0x627        R       Alef
 
1170
};
 
1171
 
 
1172
static inline int getShape(uchar cell, int shape)
 
1173
{
 
1174
    // the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here
 
1175
    uint ch = (cell != 0x49)
 
1176
              ? (shape ? arabicUnicodeMapping[cell][0] + shape : 0x600+cell)
 
1177
              : alefMaksura[shape] ;
 
1178
    return ch;
 
1179
}
 
1180
 
 
1181
 
 
1182
/*
 
1183
  Two small helper functions for arabic shaping.
 
1184
*/
 
1185
static inline const QChar prevChar(const QString *str, int pos)
 
1186
{
 
1187
    //qDebug("leftChar: pos=%d", pos);
 
1188
    pos--;
 
1189
    const QChar *ch = str->unicode() + pos;
 
1190
    while(pos > -1) {
 
1191
        if(::category(*ch) != QChar::Mark_NonSpacing)
 
1192
            return *ch;
 
1193
        pos--;
 
1194
        ch--;
 
1195
    }
 
1196
    return QChar::ReplacementCharacter;
 
1197
}
 
1198
 
 
1199
static inline const QChar nextChar(const QString *str, int pos)
 
1200
{
 
1201
    pos++;
 
1202
    int len = str->length();
 
1203
    const QChar *ch = str->unicode() + pos;
 
1204
    while(pos < len) {
 
1205
        //qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining());
 
1206
        if(::category(*ch) != QChar::Mark_NonSpacing)
 
1207
            return *ch;
 
1208
        // assume it's a transparent char, this might not be 100% correct
 
1209
        pos++;
 
1210
        ch++;
 
1211
    }
 
1212
    return QChar::ReplacementCharacter;
 
1213
}
 
1214
 
 
1215
 
 
1216
static void shapedString(const QString *uc, int from, int len, QChar *shapeBuffer, int *shapedLength,
 
1217
                         bool reverse, QGlyphLayout *glyphs, unsigned short *logClusters)
 
1218
{
 
1219
    Q_ASSERT(uc->length() >= from + len);
 
1220
 
 
1221
    if(len == 0) {
 
1222
        *shapedLength = 0;
 
1223
        return;
 
1224
    }
 
1225
 
 
1226
    QVarLengthArray<ArabicProperties> props(len+2);
 
1227
    ArabicProperties *properties = props.data();
 
1228
    int f = from;
 
1229
    int l = len;
 
1230
    if (from > 0) {
 
1231
        --f;
 
1232
        ++l;
 
1233
        ++properties;
 
1234
    }
 
1235
    if (f + l < uc->length()) {
 
1236
        ++l;
 
1237
    }
 
1238
    getArabicProperties((const unsigned short *)(uc->unicode()+f), l, props.data());
 
1239
 
 
1240
    const QChar *ch = uc->unicode() + from;
 
1241
    QChar *data = shapeBuffer;
 
1242
    int clusterStart = 0;
 
1243
 
 
1244
    for (int i = 0; i < len; i++) {
 
1245
        uchar r = ch->row();
 
1246
        int gpos = data - shapeBuffer;
 
1247
 
 
1248
        if (r != 0x06) {
 
1249
            if (r == 0x20) {
 
1250
                uchar c = ch->cell();
 
1251
                if (c == 0x0c || c == 0x0d)
 
1252
                    // remove ZWJ and ZWNJ
 
1253
                    goto skip;
 
1254
            }
 
1255
            if (reverse)
 
1256
                *data = mirroredChar(*ch);
 
1257
            else
 
1258
                *data = *ch;
 
1259
        } else {
 
1260
            uchar c = ch->cell();
 
1261
            int pos = i + from;
 
1262
            int shape = properties[i].shape;
 
1263
//            qDebug("mapping U+%x to shape %d glyph=0x%x", ch->unicode(), shape, getShape(c, shape));
 
1264
            // take care of lam-alef ligatures (lam right of alef)
 
1265
            ushort map;
 
1266
            switch (c) {
 
1267
                case 0x44: { // lam
 
1268
                    const QChar pch = nextChar(uc, pos);
 
1269
                    if (pch.row() == 0x06) {
 
1270
                        switch (pch.cell()) {
 
1271
                            case 0x22:
 
1272
                            case 0x23:
 
1273
                            case 0x25:
 
1274
                            case 0x27:
 
1275
//                                 qDebug(" lam of lam-alef ligature");
 
1276
                                map = arabicUnicodeLamAlefMapping[pch.cell() - 0x22][shape];
 
1277
                                goto next;
 
1278
                            default:
 
1279
                                break;
 
1280
                        }
 
1281
                    }
 
1282
                    break;
 
1283
                }
 
1284
                case 0x22: // alef with madda
 
1285
                case 0x23: // alef with hamza above
 
1286
                case 0x25: // alef with hamza below
 
1287
                case 0x27: // alef
 
1288
                    if (prevChar(uc, pos).unicode() == 0x0644) {
 
1289
                        // have a lam alef ligature
 
1290
                        //qDebug(" alef of lam-alef ligature");
 
1291
                        goto skip;
 
1292
                    }
 
1293
                default:
 
1294
                    break;
 
1295
            }
 
1296
            map = getShape(c, shape);
 
1297
        next:
 
1298
            *data = map;
 
1299
        }
 
1300
        // ##### Fixme
 
1301
        //glyphs[gpos].attributes.zeroWidth = zeroWidth;
 
1302
        if (::category(*ch) == QChar::Mark_NonSpacing) {
 
1303
            glyphs[gpos].attributes.mark = true;
 
1304
//             qDebug("glyph %d (char %d) is mark!", gpos, i);
 
1305
        } else {
 
1306
            glyphs[gpos].attributes.mark = false;
 
1307
            clusterStart = data - shapeBuffer;
 
1308
        }
 
1309
        glyphs[gpos].attributes.clusterStart = !glyphs[gpos].attributes.mark;
 
1310
        glyphs[gpos].attributes.combiningClass = combiningClass(*ch);
 
1311
        glyphs[gpos].attributes.justification = properties[i].justification;
 
1312
//         qDebug("data[%d] = %x (from %x)", gpos, (uint)data->unicode(), ch->unicode());
 
1313
        data++;
 
1314
    skip:
 
1315
        ch++;
 
1316
        logClusters[i] = clusterStart;
 
1317
    }
 
1318
    *shapedLength = data - shapeBuffer;
 
1319
}
 
1320
 
 
1321
#if defined(QT_HAVE_FREETYPE)
 
1322
 
 
1323
static bool arabicSyriacOpenTypeShape(QOpenType *openType, QShaperItem *item)
 
1324
{
 
1325
    int nglyphs = item->num_glyphs;
 
1326
    if (!item->font->stringToCMap(item->string->unicode()+item->from, item->length, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
 
1327
        return false;
 
1328
 
 
1329
    heuristicSetGlyphAttributes(item);
 
1330
 
 
1331
    QGlyphLayout *glyphs = item->glyphs;
 
1332
    unsigned short *logClusters = item->log_clusters;
 
1333
    const unsigned short *uc = (const unsigned short *)item->string->unicode() + item->from;
 
1334
 
 
1335
    QVarLengthArray<ArabicProperties> props(item->length+2);
 
1336
    ArabicProperties *properties = props.data();
 
1337
    int f = 0;
 
1338
    int l = item->length;
 
1339
    if (item->from > 0) {
 
1340
        --f;
 
1341
        ++l;
 
1342
        ++properties;
 
1343
    }
 
1344
    if (f + l < item->string->length()) {
 
1345
        ++l;
 
1346
    }
 
1347
    getArabicProperties((const unsigned short *)(uc+f), l, props.data());
 
1348
 
 
1349
    QVarLengthArray<bool> apply(item->num_glyphs);
 
1350
 
 
1351
 
 
1352
    // Hack to remove ZWJ and ZWNJ from rendered output.
 
1353
    int j = 0;
 
1354
    for (int i = 0; i < item->num_glyphs; i++) {
 
1355
        if (uc[i] == 0x200c || uc[i] == 0x200d)
 
1356
            continue;
 
1357
        glyphs[j] = glyphs[i];
 
1358
        properties[j] = properties[i];
 
1359
        glyphs[j].attributes.justification = properties[i].justification;
 
1360
        logClusters[i] = logClusters[j];
 
1361
        ++j;
 
1362
    }
 
1363
    item->num_glyphs = j;
 
1364
 
 
1365
    openType->init(item);
 
1366
 
 
1367
    // call features in the order defined by http://www.microsoft.com/typography/otfntdev/arabicot/shaping.htm
 
1368
    openType->applyGSUBFeature(FT_MAKE_TAG('c', 'c', 'm', 'p'));
 
1369
 
 
1370
    if (item->script == QUnicodeTables::Arabic) {
 
1371
        const struct {
 
1372
            int tag;
 
1373
            int shape;
 
1374
        } features[] = {
 
1375
            { FT_MAKE_TAG('i', 's', 'o', 'l'), XIsolated },
 
1376
            { FT_MAKE_TAG('f', 'i', 'n', 'a'), XFinal },
 
1377
            { FT_MAKE_TAG('m', 'e', 'd', 'i'), XMedial },
 
1378
            { FT_MAKE_TAG('i', 'n', 'i', 't'), XInitial }
 
1379
        };
 
1380
        for (int j = 0; j < 4; ++j) {
 
1381
            for (int i = 0; i < item->num_glyphs; i++)
 
1382
                apply[i] = (properties[i].shape == features[j].shape);
 
1383
            openType->applyGSUBFeature(features[j].tag, apply.data());
 
1384
        }
 
1385
    } else {
 
1386
        const struct {
 
1387
            int tag;
 
1388
            int shape;
 
1389
        } features[] = {
 
1390
            { FT_MAKE_TAG('i', 's', 'o', 'l'), XIsolated },
 
1391
            { FT_MAKE_TAG('f', 'i', 'n', 'a'), XFinal },
 
1392
            { FT_MAKE_TAG('f', 'i', 'n', '2'), XFinal },
 
1393
            { FT_MAKE_TAG('f', 'i', 'n', '3'), XFinal },
 
1394
            { FT_MAKE_TAG('m', 'e', 'd', 'i'), XMedial },
 
1395
            { FT_MAKE_TAG('m', 'e', 'd', '2'), XMedial },
 
1396
            { FT_MAKE_TAG('i', 'n', 'i', 't'), XInitial }
 
1397
        };
 
1398
        for (int j = 0; j < 7; ++j) {
 
1399
            for (int i = 0; i < item->num_glyphs; i++)
 
1400
                apply[i] = (properties[i].shape == features[j].shape);
 
1401
            openType->applyGSUBFeature(features[j].tag, apply.data());
 
1402
        }
 
1403
    }
 
1404
    const int commonFeatures[] = {
 
1405
        // these features get applied to all glyphs and both scripts
 
1406
        FT_MAKE_TAG('r', 'l', 'i', 'g'),
 
1407
        FT_MAKE_TAG('c', 'a', 'l', 't'),
 
1408
        FT_MAKE_TAG('l', 'i', 'g', 'a'),
 
1409
        FT_MAKE_TAG('d', 'l', 'i', 'g')
 
1410
    };
 
1411
    for (int j = 0; j < 4; ++j)
 
1412
        openType->applyGSUBFeature(commonFeatures[j]);
 
1413
 
 
1414
    if (item->script == QUnicodeTables::Arabic) {
 
1415
        const int features[] = {
 
1416
            FT_MAKE_TAG('c', 's', 'w', 'h'),
 
1417
            // mset is used in old Win95 fonts that don't have a 'mark' positioning table.
 
1418
            FT_MAKE_TAG('m', 's', 'e', 't')
 
1419
        };
 
1420
        for (int j = 0; j < 2; ++j)
 
1421
            openType->applyGSUBFeature(features[j]);
 
1422
    }
 
1423
 
 
1424
    openType->applyGPOSFeatures();
 
1425
 
 
1426
    // reset num_glyphs to what is available.
 
1427
    item->num_glyphs = nglyphs;
 
1428
    return openType->appendTo(item);
 
1429
}
 
1430
 
 
1431
#endif
 
1432
 
 
1433
static void arabic_attributes(int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes)
 
1434
{
 
1435
    const QChar *uc = text.unicode() + from;
 
1436
    attributes += from;
 
1437
    for (int i = 0; i < len; i++) {
 
1438
        QChar::Category cat = ::category(*uc);
 
1439
        attributes->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
 
1440
        attributes->softBreak = false;
 
1441
        attributes->charStop = (cat != QChar::Mark_NonSpacing);
 
1442
        attributes->wordStop = false;
 
1443
        attributes->invalid = false;
 
1444
        ++uc;
 
1445
        ++attributes;
 
1446
    }
 
1447
}
 
1448
 
 
1449
 
 
1450
// #### stil missing: identify invalid character combinations
 
1451
static bool arabic_shape(QShaperItem *item)
 
1452
{
 
1453
    Q_ASSERT(item->script == QUnicodeTables::Arabic);
 
1454
 
 
1455
#ifdef QT_HAVE_FREETYPE
 
1456
    QOpenType *openType = item->font->openType();
 
1457
 
 
1458
    if (openType && openType->supportsScript(QUnicodeTables::Arabic))
 
1459
        return arabicSyriacOpenTypeShape(openType, item);
 
1460
#endif
 
1461
 
 
1462
    QVarLengthArray<ushort> shapedChars(item->length);
 
1463
 
 
1464
    int slen;
 
1465
    shapedString(item->string, item->from, item->length, (QChar *)shapedChars.data(), &slen,
 
1466
                  item->flags & QTextEngine::RightToLeft,
 
1467
                  item->glyphs, item->log_clusters);
 
1468
 
 
1469
    if (!item->font->stringToCMap((QChar *)shapedChars.data(), slen, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
 
1470
        return false;
 
1471
 
 
1472
    for (int i = 0; i < slen; ++i)
 
1473
        if (item->glyphs[i].attributes.mark)
 
1474
            item->glyphs[i].advance = QPointF();
 
1475
    qt_heuristicPosition(item);
 
1476
    return true;
 
1477
}
 
1478
 
 
1479
#if defined(Q_WS_X11) || defined(Q_WS_QWS)
 
1480
# include "qscriptengine_unix.cpp"
 
1481
#elif defined(Q_WS_WIN)
 
1482
# include "qscriptengine_win.cpp"
 
1483
#elif defined(Q_WS_MAC)
 
1484
# include "qscriptengine_mac.cpp"
 
1485
#endif