source: trunk/src/gui/text/qtextengine_mac.cpp@ 651

Last change on this file since 651 was 651, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.2 sources.

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