Fix uses of qRound on non-floating-point types.
[profile/ivi/qtbase.git] / src / gui / text / qtextengine_mac.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 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     if (!fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters, attributes(), &si)) {
609         ensureSpace(num_glyphs);
610         g = availableGlyphs(&si);
611         stringToCMapFailed = !fe->stringToCMap(str, len, &g, &num_glyphs, flags, log_clusters,
612                                                attributes(), &si);
613     }
614
615     if (!stringToCMapFailed) {
616         heuristicSetGlyphAttributes(str, len, &g, log_clusters, num_glyphs);
617
618         si.num_glyphs = num_glyphs;
619
620         layoutData->used += si.num_glyphs;
621
622         QGlyphLayout g = shapedGlyphs(&si);
623
624         if (si.analysis.script == QUnicodeTables::Arabic) {
625             QVarLengthArray<QArabicProperties> props(len + 2);
626             QArabicProperties *properties = props.data();
627             int f = si.position;
628             int l = len;
629             if (f > 0) {
630                 --f;
631                 ++l;
632                 ++properties;
633             }
634             if (f + l < layoutData->string.length()) {
635                 ++l;
636             }
637             qt_getArabicProperties((const unsigned short *)(layoutData->string.unicode()+f), l, props.data());
638
639             unsigned short *log_clusters = logClusters(&si);
640
641             for (int i = 0; i < len; ++i) {
642                 int gpos = log_clusters[i];
643                 g.attributes[gpos].justification = properties[i].justification;
644             }
645         }
646     }
647
648     const ushort *uc = reinterpret_cast<const ushort *>(str);
649
650     if ((si.analysis.flags == QScriptAnalysis::SmallCaps || si.analysis.flags == QScriptAnalysis::Uppercase
651          || si.analysis.flags == QScriptAnalysis::Lowercase)
652         && uc != upperCased)
653         delete [] uc;
654 }
655
656 QT_END_NAMESPACE