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