1
/****************************************************************************
2
**
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
6
**
7
** This file is part of the QtGui module of the Qt Toolkit.
8
**
9
** $QT_BEGIN_LICENSE:LGPL$
10
** GNU Lesser General Public License Usage
11
** This file may be used under the terms of the GNU Lesser General Public
12
** License version 2.1 as published by the Free Software Foundation and
13
** appearing in the file LICENSE.LGPL included in the packaging of this
14
** file. Please review the following information to ensure the GNU Lesser
15
** General Public License version 2.1 requirements will be met:
16
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17
**
18
** In addition, as a special exception, Nokia gives you certain additional
19
** rights. These rights are described in the Nokia Qt LGPL Exception
20
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21
**
22
** GNU General Public License Usage
23
** Alternatively, this file may be used under the terms of the GNU General
24
** Public License version 3.0 as published by the Free Software Foundation
25
** and appearing in the file LICENSE.GPL included in the packaging of this
26
** file. Please review the following information to ensure the GNU General
27
** Public License version 3.0 requirements will be met:
28
** http://www.gnu.org/copyleft/gpl.html.
29
**
30
** Other Usage
31
** Alternatively, this file may be used in accordance with the terms and
32
** conditions contained in a signed written agreement between you and Nokia.
33
**
34
**
35
**
36
**
37
**
38
** $QT_END_LICENSE$
39
**
40
****************************************************************************/
41
42
#include "qtextengine_p.h"
43
44
#include <private/qfontengine_coretext_p.h>
45
#include <private/qfontengine_mac_p.h>
46
47
QT_BEGIN_NAMESPACE
48
49
// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
50
// and no reordering.
51
// also computes logClusters heuristically
52
static void heuristicSetGlyphAttributes(const QChar *uc, int length, QGlyphLayout *glyphs, unsigned short *logClusters, int num_glyphs)
53
{
54
    // ### zeroWidth and justification are missing here!!!!!
55
56
    Q_UNUSED(num_glyphs);
57
58
//     qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
59
60
    const bool symbolFont = false; // ####
61
    glyphs->attributes[0].mark = false;
62
    glyphs->attributes[0].clusterStart = true;
63
    glyphs->attributes[0].dontPrint = (!symbolFont && uc[0].unicode() == 0x00ad) || qIsControlChar(uc[0].unicode());
64
65
    int pos = 0;
66
    int lastCat = QChar::category(uc[0].unicode());
67
    for (int i = 1; i < length; ++i) {
68
        if (logClusters[i] == pos)
69
            // same glyph
70
            continue;
71
        ++pos;
72
        while (pos < logClusters[i]) {
73
            ++pos;
74
        }
75
        // hide soft-hyphens by default
76
        if ((!symbolFont && uc[i].unicode() == 0x00ad) || qIsControlChar(uc[i].unicode()))
77
            glyphs->attributes[pos].dontPrint = true;
78
        const QUnicodeTables::Properties *prop = QUnicodeTables::properties(uc[i].unicode());
79
        int cat = prop->category;
80
81
        // one gets an inter character justification point if the current char is not a non spacing mark.
82
        // as then the current char belongs to the last one and one gets a space justification point
83
        // after the space char.
84
        if (lastCat == QChar::Separator_Space)
85
            glyphs->attributes[pos-1].justification = HB_Space;
86
        else if (cat != QChar::Mark_NonSpacing)
87
            glyphs->attributes[pos-1].justification = HB_Character;
88
        else
89
            glyphs->attributes[pos-1].justification = HB_NoJustification;
90
91
        lastCat = cat;
92
    }
93
    pos = logClusters[length-1];
94
    if (lastCat == QChar::Separator_Space)
95
        glyphs->attributes[pos].justification = HB_Space;
96
    else
97
        glyphs->attributes[pos].justification = HB_Character;
98
}
99
100
struct QArabicProperties {
101
    unsigned char shape;
102
    unsigned char justification;
103
};
104
Q_DECLARE_TYPEINFO(QArabicProperties, Q_PRIMITIVE_TYPE);
105
106
enum QArabicShape {
107
    XIsolated,
108
    XFinal,
109
    XInitial,
110
    XMedial,
111
    // intermediate state
112
    XCausing
113
};
114
115
116
// these groups correspond to the groups defined in the Unicode standard.
117
// Some of these groups are equal with regards to both joining and line breaking behaviour,
118
// and thus have the same enum value
119
//
120
// I'm not sure the mapping of syriac to arabic enums is correct with regards to justification, but as
121
// I couldn't find any better document I'll hope for the best.
122
enum ArabicGroup {
123
    // NonJoining
124
    ArabicNone,
125
    ArabicSpace,
126
    // Transparent
127
    Transparent,
128
    // Causing
129
    Center,
130
    Kashida,
131
132
    // Arabic
133
    // Dual
134
    Beh,
135
    Noon,
136
    Meem = Noon,
137
    Heh = Noon,
138
    KnottedHeh = Noon,
139
    HehGoal = Noon,
140
    SwashKaf = Noon,
141
    Yeh,
142
    Hah,
143
    Seen,
144
    Sad = Seen,
145
    Tah,
146
    Kaf = Tah,
147
    Gaf = Tah,
148
    Lam = Tah,
149
    Ain,
150
    Feh = Ain,
151
    Qaf = Ain,
152
    // Right
153
    Alef,
154
    Waw,
155
    Dal,
156
    TehMarbuta = Dal,
157
    Reh,
158
    HamzaOnHehGoal,
159
    YehWithTail = HamzaOnHehGoal,
160
    YehBarre = HamzaOnHehGoal,
161
162
    // Syriac
163
    // Dual
164
    Beth = Beh,
165
    Gamal = Ain,
166
    Heth = Noon,
167
    Teth = Hah,
168
    Yudh = Noon,
169
    Kaph = Noon,
170
    Lamadh = Lam,
171
    Mim = Noon,
172
    Nun = Noon,
173
    Semakh = Noon,
174
    FinalSemakh = Noon,
175
    SyriacE = Ain,
176
    Pe = Ain,
177
    ReversedPe = Hah,
178
    Qaph = Noon,
179
    Shin = Noon,
180
    Fe = Ain,
181
182
    // Right
183
    Alaph = Alef,
184
    Dalath = Dal,
185
    He = Dal,
186
    SyriacWaw = Waw,
187
    Zain = Alef,
188
    YudhHe = Waw,
189
    Sadhe = HamzaOnHehGoal,
190
    Taw = Dal,
191
192
    // Compiler bug? Otherwise ArabicGroupsEnd would be equal to Dal + 1.
193
    Dummy = HamzaOnHehGoal,
194
    ArabicGroupsEnd
195
};
196
197
static const unsigned char arabic_group[0x150] = {
198
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
199
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
200
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
201
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
202
203
    Transparent, Transparent, Transparent, Transparent,
204
    Transparent, Transparent, ArabicNone, ArabicNone,
205
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
206
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
207
208
    ArabicNone, ArabicNone, Alef, Alef,
209
    Waw, Alef, Yeh, Alef,
210
    Beh, TehMarbuta, Beh, Beh,
211
    Hah, Hah, Hah, Dal,
212
213
    Dal, Reh, Reh, Seen,
214
    Seen, Sad, Sad, Tah,
215
    Tah, Ain, Ain, ArabicNone,
216
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
217
218
    // 0x640
219
    Kashida, Feh, Qaf, Kaf,
220
    Lam, Meem, Noon, Heh,
221
    Waw, Yeh, Yeh, Transparent,
222
    Transparent, Transparent, Transparent, Transparent,
223
224
    Transparent, Transparent, Transparent, Transparent,
225
    Transparent, Transparent, Transparent, Transparent,
226
    Transparent, ArabicNone, ArabicNone, ArabicNone,
227
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
228
229
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
230
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
231
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
232
    ArabicNone, ArabicNone, Beh, Qaf,
233
234
    Transparent, Alef, Alef, Alef,
235
    ArabicNone, Alef, Waw, Waw,
236
    Yeh, Beh, Beh, Beh,
237
    Beh, Beh, Beh, Beh,
238
239
    // 0x680
240
    Beh, Hah, Hah, Hah,
241
    Hah, Hah, Hah, Hah,
242
    Dal, Dal, Dal, Dal,
243
    Dal, Dal, Dal, Dal,
244
245
    Dal, Reh, Reh, Reh,
246
    Reh, Reh, Reh, Reh,
247
    Reh, Reh, Seen, Seen,
248
    Seen, Sad, Sad, Tah,
249
250
    Ain, Feh, Feh, Feh,
251
    Feh, Feh, Feh, Qaf,
252
    Qaf, Gaf, SwashKaf, Gaf,
253
    Kaf, Kaf, Kaf, Gaf,
254
255
    Gaf, Gaf, Gaf, Gaf,
256
    Gaf, Lam, Lam, Lam,
257
    Lam, Noon, Noon, Noon,
258
    Noon, Noon, KnottedHeh, Hah,
259
260
    // 0x6c0
261
    TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
262
    Waw, Waw, Waw, Waw,
263
    Waw, Waw, Waw, Waw,
264
    Yeh, YehWithTail, Yeh, Waw,
265
266
    Yeh, Yeh, YehBarre, YehBarre,
267
    ArabicNone, TehMarbuta, Transparent, Transparent,
268
    Transparent, Transparent, Transparent, Transparent,
269
    Transparent, ArabicNone, ArabicNone, Transparent,
270
271
    Transparent, Transparent, Transparent, Transparent,
272
    Transparent, ArabicNone, ArabicNone, Transparent,
273
    Transparent, ArabicNone, Transparent, Transparent,
274
    Transparent, Transparent, Dal, Reh,
275
276
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
277
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
278
    ArabicNone, ArabicNone, Seen, Sad,
279
    Ain, ArabicNone, ArabicNone, KnottedHeh,
280
281
    // 0x700
282
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
283
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
284
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
285
    ArabicNone, ArabicNone, ArabicNone, ArabicNone,
286
287
    Alaph, Transparent, Beth, Gamal,
288
    Gamal, Dalath, Dalath, He,
289
    SyriacWaw, Zain, Heth, Teth,
290
    Teth, Yudh, YudhHe, Kaph,
291
292
    Lamadh, Mim, Nun, Semakh,
293
    FinalSemakh, SyriacE, Pe, ReversedPe,
294
    Sadhe, Qaph, Dalath, Shin,
295
    Taw, Beth, Gamal, Dalath,
296
297
    Transparent, Transparent, Transparent, Transparent,
298
    Transparent, Transparent, Transparent, Transparent,
299
    Transparent, Transparent, Transparent, Transparent,
300
    Transparent, Transparent, Transparent, Transparent,
301
302
    Transparent, Transparent, Transparent, Transparent,
303
    Transparent, Transparent, Transparent, Transparent,
304
    Transparent, Transparent, Transparent, ArabicNone,
305
    ArabicNone, Zain, Kaph, Fe,
306
};
307
308
static inline ArabicGroup arabicGroup(unsigned short uc)
309
{
310
    if (uc >= 0x0600 && uc < 0x750)
311
        return (ArabicGroup) arabic_group[uc-0x600];
312
    else if (uc == 0x200d)
313
        return Center;
314
    else if (QChar::category(uc) == QChar::Separator_Space)
315
        return ArabicSpace;
316
    else
317
        return ArabicNone;
318
}
319
320
321
/*
322
   Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
323
   arabic).
324
325
   Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
326
   transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
327
328
   Right join-causing: dual + center
329
   Left join-causing: dual + right + center
330
331
   Rules are as follows (for a string already in visual order, as we have it here):
332
333
   R1 Transparent characters do not affect joining behaviour.
334
   R2 A right joining character, that has a right join-causing char on the right will get form XRight
335
   (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
336
   Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
337
   R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
338
             the right will get form XMedial
339
   R5  A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
340
         will get form XRight
341
   R6 A dual joining character, that has a  left join causing char on the left, and no right join causing char on the right
342
         will get form XLeft
343
   R7 Otherwise the character will get form XIsolated
344
345
   Additionally we have to do the minimal ligature support for lam-alef ligatures:
346
347
   L1 Transparent characters do not affect ligature behaviour.
348
   L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
349
   L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
350
351
   The state table below handles rules R1-R7.
352
*/
353
354
enum Joining {
355
    JNone,
356
    JCausing,
357
    JDual,
358
    JRight,
359
    JTransparent
360
};
361
362
static const Joining joining_for_group[ArabicGroupsEnd] = {
363
    // NonJoining
364
    JNone, // ArabicNone
365
    JNone, // ArabicSpace
366
    // Transparent
367
    JTransparent, // Transparent
368
    // Causing
369
    JCausing, // Center
370
    JCausing, // Kashida
371
    // Dual
372
    JDual, // Beh
373
    JDual, // Noon
374
    JDual, // Yeh
375
    JDual, // Hah
376
    JDual, // Seen
377
    JDual, // Tah
378
    JDual, // Ain
379
    // Right
380
    JRight, // Alef
381
    JRight, // Waw
382
    JRight, // Dal
383
    JRight, // Reh
384
    JRight  // HamzaOnHehGoal
385
};
386
387
388
struct JoiningPair {
389
    QArabicShape form1;
390
    QArabicShape form2;
391
};
392
393
static const JoiningPair joining_table[5][4] =
394
// None, Causing, Dual, Right
395
{
396
    { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } }, // XIsolated
397
    { { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } }, // XFinal
398
    { { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } }, // XInitial
399
    { { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } }, // XMedial
400
    { { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } }, // XCausing
401
};
402
403
404
/*
405
According to http://www.microsoft.com/middleeast/Arabicdev/IE6/KBase.asp
406
407
1. Find the priority of the connecting opportunities in each word
408
2. Add expansion at the highest priority connection opportunity
409
3. If more than one connection opportunity have the same highest value,
410
   use the opportunity closest to the end of the word.
411
412
Following is a chart that provides the priority for connection
413
opportunities and where expansion occurs. The character group names
414
are those in table 6.6 of the UNICODE 2.0 book.
415
416
417
PrioritY        Glyph                   Condition                                       Kashida Location
418
419
Arabic_Kashida        User inserted Kashida   The user entered a Kashida in a position.       After the user
420
                (Shift+j or Shift+[E with hat])    Thus, it is the highest priority to insert an   inserted kashida
421
                                        automatic kashida.
422
423
Arabic_Seen        Seen, Sad               Connecting to the next character.               After the character.
424
                                        (Initial or medial form).
425
426
Arabic_HaaDal        Teh Marbutah, Haa, Dal  Connecting to previous character.               Before the final form
427
                                                                                        of these characters.
428
429
Arabic_Alef     Alef, Tah, Lam,         Connecting to previous character.               Before the final form
430
                Kaf and Gaf                                                             of these characters.
431
432
Arabic_BaRa     Reh, Yeh                Connected to medial Beh                         Before preceding medial Baa
433
434
Arabic_Waw        Waw, Ain, Qaf, Feh      Connecting to previous character.               Before the final form of
435
                                                                                        these characters.
436
437
Arabic_Normal   Other connecting        Connecting to previous character.               Before the final form
438
                characters                                                              of these characters.
439
440
441
442
This seems to imply that we have at most one kashida point per arabic word.
443
444
*/
445
446
void qt_getArabicProperties(const unsigned short *chars, int len, QArabicProperties *properties)
447
{
448
//     qDebug("arabicSyriacOpenTypeShape: properties:");
449
    int lastPos = 0;
450
    int lastGroup = ArabicNone;
451
452
    ArabicGroup group = arabicGroup(chars[0]);
453
    Joining j = joining_for_group[group];
454
    QArabicShape shape = joining_table[XIsolated][j].form2;
455
    properties[0].justification = HB_NoJustification;
456
457
    for (int i = 1; i < len; ++i) {
458
        // #### fix handling for spaces and punktuation
459
        properties[i].justification = HB_NoJustification;
460
461
        group = arabicGroup(chars[i]);
462
        j = joining_for_group[group];
463
464
        if (j == JTransparent) {
465
            properties[i].shape = XIsolated;
466
            continue;
467
        }
468
469
        properties[lastPos].shape = joining_table[shape][j].form1;
470
        shape = joining_table[shape][j].form2;
471
472
        switch(lastGroup) {
473
        case Seen:
474
            if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
475
                properties[i-1].justification = HB_Arabic_Seen;
476
            break;
477
        case Hah:
478
            if (properties[lastPos].shape == XFinal)
479
                properties[lastPos-1].justification = HB_Arabic_HaaDal;
480
            break;
481
        case Alef:
482
            if (properties[lastPos].shape == XFinal)
483
                properties[lastPos-1].justification = HB_Arabic_Alef;
484
            break;
485
        case Ain:
486
            if (properties[lastPos].shape == XFinal)
487
                properties[lastPos-1].justification = HB_Arabic_Waw;
488
            break;
489
        case Noon:
490
            if (properties[lastPos].shape == XFinal)
491
                properties[lastPos-1].justification = HB_Arabic_Normal;
492
            break;
493
        case ArabicNone:
494
            break;
495
496
        default:
497
            Q_ASSERT(false);
498
        }
499
500
        lastGroup = ArabicNone;
501
502
        switch(group) {
503
        case ArabicNone:
504
        case Transparent:
505
        // ### Center should probably be treated as transparent when it comes to justification.
506
        case Center:
507
            break;
508
        case ArabicSpace:
509
            properties[i].justification = HB_Arabic_Space;
510
            break;
511
        case Kashida:
512
            properties[i].justification = HB_Arabic_Kashida;
513
            break;
514
        case Seen:
515
            lastGroup = Seen;
516
            break;
517
518
        case Hah:
519
        case Dal:
520
            lastGroup = Hah;
521
            break;
522
523
        case Alef:
524
        case Tah:
525
            lastGroup = Alef;
526
            break;
527
528
        case Yeh:
529
        case Reh:
530
            if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
531
                properties[lastPos-1].justification = HB_Arabic_BaRa;
532
            break;
533
534
        case Ain:
535
        case Waw:
536
            lastGroup = Ain;
537
            break;
538
539
        case Noon:
540
        case Beh:
541
        case HamzaOnHehGoal:
542
            lastGroup = Noon;
543
            break;
544
        case ArabicGroupsEnd:
545
            Q_ASSERT(false);
546
        }
547
548
        lastPos = i;
549
    }
550
    properties[lastPos].shape = joining_table[shape][JNone].form1;
551
552
553
//     for (int i = 0; i < len; ++i)
554
//         qDebug("arabic properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
555
}
556
557
void QTextEngine::shapeTextMac(int item) const
558
{
559
    QScriptItem &si = layoutData->items[item];
560
561
    si.glyph_data_offset = layoutData->used;
562
563
    QFontEngine *font = fontEngine(si, &si.ascent, &si.descent, &si.leading);
564
    if (font->type() != QFontEngine::Multi) {
565
        shapeTextWithHarfbuzz(item);
566
        return;
567
    }
568
    
569
#ifndef QT_MAC_USE_COCOA
570
    QFontEngineMacMulti *fe = static_cast<QFontEngineMacMulti *>(font);
571
#else
572
    QCoreTextFontEngineMulti *fe = static_cast<QCoreTextFontEngineMulti *>(font);
573
#endif
574
    QTextEngine::ShaperFlags flags;
575
    if (si.analysis.bidiLevel % 2)
576
        flags |= RightToLeft;
577
    if (option.useDesignMetrics())
578
	flags |= DesignMetrics;
579
580
    attributes(); // pre-initialize char attributes
581
582
    const int len = length(item);
583
    int num_glyphs = length(item);
584
    const QChar *str = layoutData->string.unicode() + si.position;
585
    ushort upperCased[256];
586
    if (si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
587
            || si.analysis.flags == QScriptAnalysis::Lowercase) {
588
        ushort *uc = upperCased;
589
        if (len > 256)
590
            uc = new ushort[len];
591
        for (int i = 0; i < len; ++i) {
592
            if(si.analysis.flags == QScriptAnalysis::Lowercase)
593
                uc[i] = str[i].toLower().unicode();
594
            else
595
                uc[i] = str[i].toUpper().unicode();
596
        }
597
        str = reinterpret_cast<const QChar *>(uc);
598
    }
599
600
    ensureSpace(num_glyphs);
601
    num_glyphs = layoutData->glyphLayout.numGlyphs - layoutData->used;
602
603
    QGlyphLayout g = availableGlyphs(&si);
604
    g.numGlyphs = num_glyphs;
605
    unsigned short *log_clusters = logClusters(&si);
606
607
    bool stringToCMapFailed = false;
608
    // Skip shaping of line or paragraph separators since we are not
609
    // going to draw them anyway
610
    if (si.analysis.flags == QScriptAnalysis::LineOrParagraphSeparator
611
        && !(option.flags() & QTextOption::ShowLineAndParagraphSeparators)) {
612
        memset(log_clusters, 0, len * sizeof(unsigned short));
613
        goto cleanUp;
614
    }
615
616
    if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes(), &si)) {
617
        ensureSpace(num_glyphs);
618
        g = availableGlyphs(&si);
619
        stringToCMapFailed = !fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters,
620
                                               attributes(), &si);
621
    }
622
623
    if (!stringToCMapFailed) {
624
        heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs);
625
626
        si.num_glyphs = num_glyphs;
627
628
        layoutData->used += si.num_glyphs;
629
630
        QGlyphLayout g = shapedGlyphs(&si);
631
632
        if (si.analysis.script == QUnicodeTables::Arabic) {
633
            QVarLengthArray<QArabicProperties> props(len + 2);
634
            QArabicProperties *properties = props.data();
635
            int f = si.position;
636
            int l = len;
637
            if (f > 0) {
638
                --f;
639
                ++l;
640
                ++properties;
641
            }
642
            if (f + l < layoutData->string.length()) {
643
                ++l;
644
            }
645
            qt_getArabicProperties((const unsigned short *)(layoutData->string.unicode()+f), l, props.data());
646
647
            unsigned short *log_clusters = logClusters(&si);
648
649
            for (int i = 0; i < len; ++i) {
650
                int gpos = log_clusters[i];
651
                g.attributes[gpos].justification = properties[i].justification;
652
            }
653
        }
654
    }
655
656
cleanUp:
657
    const ushort *uc = reinterpret_cast<const ushort *>(str);
658
659
    if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
660
         || si.analysis.flags == QScriptAnalysis::Lowercase)
661
        && uc != upperCased)
662
        delete [] uc;
663
}
664
665
QT_END_NAMESPACE