1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
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
heuristicSetGlyphAttributes(const QChar * uc,int length,QGlyphLayout * glyphs,unsigned short * logClusters,int num_glyphs)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 
arabicGroup(unsigned short uc)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 
qt_getArabicProperties(const unsigned short * chars,int len,QArabicProperties * properties)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 
shapeTextMac(int item) const557 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
666