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

Last change on this file since 352 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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