1
/****************************************************************************
3
** Copyright (C) 1992-2005 Trolltech AS. All rights reserved.
5
** This file is part of the text module of the Qt Toolkit.
7
** This file may be distributed under the terms of the Q Public License
8
** as defined by Trolltech AS of Norway and appearing in the file
9
** LICENSE.QPL included in the packaging of this file.
11
** This file may be distributed and/or modified under the terms of the
12
** GNU General Public License version 2 as published by the Free Software
13
** Foundation and appearing in the file LICENSE.GPL included in the
14
** packaging of this file.
16
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
17
** information about Qt Commercial License Agreements.
18
** See http://www.trolltech.com/qpl/ for QPL licensing information.
19
** See http://www.trolltech.com/gpl/ for GPL licensing information.
21
** Contact info@trolltech.com if any conditions of this licensing are
24
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
25
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
27
****************************************************************************/
29
#include "qscriptengine_p.h"
34
#include <private/qunicodetables_p.h>
35
#include "qtextengine_p.h"
36
#include "qfontengine_p.h"
38
#include <qvarlengtharray.h>
39
#ifdef QT_HAVE_FREETYPE
40
#include "qopentype_p.h"
48
// --------------------------------------------------------------------------------------------------------------------------------------------
52
// --------------------------------------------------------------------------------------------------------------------------------------------
54
static inline void positionCluster(QShaperItem *item, int gfrom, int glast)
56
int nmarks = glast - gfrom;
58
qWarning("positionCluster: no marks to position!");
62
QGlyphLayout *glyphs = item->glyphs;
63
QFontEngine *f = item->font;
65
glyph_metrics_t baseInfo = f->boundingBox(glyphs[gfrom].glyph);
67
if (item->script == QUnicodeTables::Hebrew)
68
// we need to attach below the baseline, because of the hebrew iud.
69
baseInfo.height = qMax(baseInfo.height, -baseInfo.y);
71
QRectF baseRect(baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height);
73
// qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
74
// qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
76
qreal size = f->ascent()/10.;
77
qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
78
// qDebug("offset = %f", offsetBase);
80
bool rightToLeft = item->flags & QTextEngine::RightToLeft;
83
unsigned char lastCmb = 0;
84
QRectF attachmentRect;
86
for(i = 1; i <= nmarks; i++) {
87
glyph_t mark = glyphs[gfrom+i].glyph;
89
glyph_metrics_t markInfo = f->boundingBox(mark);
90
QRectF markRect(markInfo.x, markInfo.y, markInfo.width, markInfo.height);
91
// qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
93
qreal offset = offsetBase;
94
unsigned char cmb = glyphs[gfrom+i].attributes.combiningClass;
96
// ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
97
// bits in the glyphAttributes structure.
99
// fixed position classes. We approximate by mapping to one of the others.
100
// currently I added only the ones for arabic, hebrew, lao and thai.
102
// for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
104
// add a bit more offset to arabic, a bit hacky
105
if (cmb >= 27 && cmb <= 36 && offset < 3)
108
if ((cmb >= 10 && cmb <= 18) ||
109
cmb == 20 || cmb == 22 ||
110
cmb == 29 || cmb == 32)
111
cmb = QChar::Combining_Below;
113
else if (cmb == 23 || cmb == 27 || cmb == 28 ||
114
cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
115
cmb = QChar::Combining_Above;
117
else if (cmb == 9 || cmb == 103 || cmb == 118)
118
cmb = QChar::Combining_BelowRight;
120
else if (cmb == 24 || cmb == 107 || cmb == 122)
121
cmb = QChar::Combining_AboveRight;
123
cmb = QChar::Combining_AboveLeft;
129
// combining marks of different class don't interact. Reset the rectangle.
130
if (cmb != lastCmb) {
131
//qDebug("resetting rect");
132
attachmentRect = baseRect;
136
case QChar::Combining_DoubleBelow:
137
// ### wrong in rtl context!
138
case QChar::Combining_BelowLeft:
139
p += QPointF(0, offset);
140
case QChar::Combining_BelowLeftAttached:
141
p += attachmentRect.bottomLeft() - markRect.topLeft();
143
case QChar::Combining_Below:
144
p += QPointF(0, offset);
145
case QChar::Combining_BelowAttached:
146
p += attachmentRect.bottomLeft() - markRect.topLeft();
147
p += QPointF((attachmentRect.width() - markRect.width())/2 , 0);
149
case QChar::Combining_BelowRight:
150
p += QPointF(0, offset);
151
case QChar::Combining_BelowRightAttached:
152
p += attachmentRect.bottomRight() - markRect.topRight();
154
case QChar::Combining_Left:
155
p += QPointF(-offset, 0);
156
case QChar::Combining_LeftAttached:
158
case QChar::Combining_Right:
159
p += QPointF(offset, 0);
160
case QChar::Combining_RightAttached:
162
case QChar::Combining_DoubleAbove:
163
// ### wrong in RTL context!
164
case QChar::Combining_AboveLeft:
165
p += QPointF(0, -offset);
166
case QChar::Combining_AboveLeftAttached:
167
p += attachmentRect.topLeft() - markRect.bottomLeft();
169
case QChar::Combining_Above:
170
p += QPointF(0, -offset);
171
case QChar::Combining_AboveAttached:
172
p += attachmentRect.topLeft() - markRect.bottomLeft();
173
p += QPointF((attachmentRect.width() - markRect.width())/2 , 0);
175
case QChar::Combining_AboveRight:
176
p += QPointF(0, -offset);
177
case QChar::Combining_AboveRightAttached:
178
p += attachmentRect.topRight() - markRect.bottomRight();
181
case QChar::Combining_IotaSubscript:
185
// qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
186
markRect.translate(p.x(), p.y());
187
attachmentRect |= markRect;
190
glyphs[gfrom+i].offset = p;
192
glyphs[gfrom+i].offset.setX(p.x() - baseInfo.xoff);
193
glyphs[gfrom+i].offset.setX(p.y() - baseInfo.yoff);
195
glyphs[gfrom+i].advance = QPointF();
200
void qt_heuristicPosition(QShaperItem *item)
202
QGlyphLayout *glyphs = item->glyphs;
205
int i = item->num_glyphs;
207
if (cEnd == -1 && glyphs[i].attributes.mark) {
209
} else if (cEnd != -1 && !glyphs[i].attributes.mark) {
210
positionCluster(item, i, cEnd);
218
// set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
219
// and no reordering.
220
// also computes logClusters heuristically
221
static void heuristicSetGlyphAttributes(QShaperItem *item)
223
// ### zeroWidth and justification are missing here!!!!!
225
Q_ASSERT(item->num_glyphs <= item->length);
227
// qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
228
QGlyphLayout *glyphs = item->glyphs;
229
unsigned short *logClusters = item->log_clusters;
231
// honour the logClusters array if it exists.
232
const QChar *uc = item->string->unicode() + item->from;
236
for (int i = 0; i < item->length; i++) {
237
bool surrogate = (uc[i].unicode() >= 0xd800 && uc[i].unicode() < 0xdc00 && i < item->length-1
238
&& uc[i+1].unicode() >= 0xdc00 && uc[i+1].unicode() < 0xe000);
239
logClusters[i] = glyph_pos;
241
logClusters[++i] = glyph_pos;
245
Q_ASSERT(glyph_pos == item->num_glyphs);
247
for (int i = 0; i < item->length; ++i) {
248
bool surrogate = i > 0 && (uc[i - 1].unicode() >= 0xd800 && uc[i - 1].unicode() < 0xdc00 && uc[i].unicode() >= 0xdc00 && uc[i].unicode() < 0xe000);
249
logClusters[i] = surrogate ? i - 1 : i;
252
Q_ASSERT(item->num_glyphs == item->length);
255
// first char in a run is never (treated as) a mark
257
glyphs[0].attributes.mark = false;
258
glyphs[0].attributes.clusterStart = true;
261
QChar::Category lastCat = ::category(uc[0]);
262
for (int i = 1; i < item->length; ++i) {
263
if (logClusters[i] == pos)
267
while (pos < logClusters[i]) {
268
glyphs[pos].attributes = glyphs[pos-1].attributes;
271
QChar::Category cat = ::category(uc[i]);
272
if (cat != QChar::Mark_NonSpacing) {
273
glyphs[pos].attributes.mark = false;
274
glyphs[pos].attributes.clusterStart = true;
275
glyphs[pos].attributes.combiningClass = 0;
276
cStart = logClusters[i];
278
int cmb = combiningClass(uc[i]);
281
// Fix 0 combining classes
282
if (uc[pos].row() == 0x0e) {
284
unsigned char col = uc[pos].cell();
294
cmb = QChar::Combining_AboveRight;
295
} else if (col == 0xb1 ||
303
cmb = QChar::Combining_Above;
304
} else if (col == 0xbc) {
305
cmb = QChar::Combining_Below;
310
glyphs[pos].attributes.mark = true;
311
glyphs[pos].attributes.clusterStart = false;
312
glyphs[pos].attributes.combiningClass = cmb;
313
logClusters[i] = cStart;
314
glyphs[pos].advance = QPointF();
317
if (lastCat == QChar::Separator_Space)
318
glyphs[pos-1].attributes.justification = QGlyphLayout::Space;
319
else if (cat != QChar::Mark_NonSpacing)
320
glyphs[pos-1].attributes.justification = QGlyphLayout::Character;
322
glyphs[pos-1].attributes.justification = QGlyphLayout::NoJustification;
326
pos = logClusters[item->length-1];
327
if (lastCat == QChar::Separator_Space)
328
glyphs[pos].attributes.justification = QGlyphLayout::Space;
330
glyphs[pos].attributes.justification = QGlyphLayout::Character;
333
static bool basic_shape(QShaperItem *item)
335
if (!item->font->stringToCMap(item->string->unicode()+item->from, item->length, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
338
heuristicSetGlyphAttributes(item);
339
if (!(item->flags & QTextEngine::WidthOnly))
340
qt_heuristicPosition(item);
344
static void basic_attributes(int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes)
346
const QChar *uc = text.unicode() + from;
349
QCharAttributes *a = attributes;
351
for (int i = 0; i < len; i++) {
352
QChar::Category cat = ::category(*uc);
353
a->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
354
a->softBreak = false;
355
a->charStop = (cat != QChar::Mark_NonSpacing);
356
if (cat == QChar::Other_Surrogate) {
357
// char stop only on first pair
358
a->charStop = (uc->unicode() >= 0xd800 && uc->unicode() < 0xdc00 && i < len-1
359
&& uc[1].unicode() >= 0xdc00 && uc[1].unicode() < 0xe000);
361
a->wordStop = (*uc == QChar::LineSeparator);
368
#if defined(Q_WS_QWS)
369
static bool unicode_shape(QShaperItem *item)
371
QString s = item->string->mid(item->from, item->length);
372
QChar *c = (QChar *)s.unicode();
373
for (int i = 0; i < item->length; ++i) {
374
ushort uc = c[i].unicode();
375
if (uc >= 0x200e && uc < 0x2070) {
377
|| (uc >= 0x2028 && uc <= 0x202f)
382
if (!item->font->stringToCMap(s.unicode(), item->length, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
384
const QChar *cc = item->string->unicode() + item->from;
385
for (int i = 0; i < item->length; ++i) {
386
ushort uc = cc[i].unicode();
387
if (uc >= 0x200e && uc < 0x2070) {
389
|| (uc >= 0x2028 && uc <= 0x202f)
391
item->glyphs[i].advance = QPointF();
395
heuristicSetGlyphAttributes(item);
396
if (!(item->flags & QTextEngine::WidthOnly))
397
qt_heuristicPosition(item);
403
// --------------------------------------------------------------------------------------------------------------------------------------------
405
// Middle eastern languages
407
// --------------------------------------------------------------------------------------------------------------------------------------------
410
// these groups correspond to the groups defined in the Unicode standard.
411
// Some of these groups are equal whith regards to both joining and line breaking behaviour,
412
// and thus have the same enum value
414
// I'm not sure the mapping of syriac to arabic enums is correct with regards to justification, but as
415
// I couldn't find any better document I'll hope for the best.
453
YehWithTail = HamzaOnHehGoal,
454
YehBarre = HamzaOnHehGoal,
483
Sadhe = HamzaOnHehGoal,
486
// Compiler bug? Otherwise ArabicGroupsEnd would be equal to Dal + 1.
487
Dummy = HamzaOnHehGoal,
491
static const unsigned char arabic_group[0x150] = {
492
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
493
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
494
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
495
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
497
Transparent, Transparent, Transparent, Transparent,
498
Transparent, Transparent, ArabicNone, ArabicNone,
499
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
500
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
502
ArabicNone, ArabicNone, Alef, Alef,
503
Waw, Alef, Yeh, Alef,
504
Beh, TehMarbuta, Beh, Beh,
509
Tah, Ain, Ain, ArabicNone,
510
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
513
Kashida, Feh, Qaf, Kaf,
514
Lam, Meem, Noon, Heh,
515
Waw, Yeh, Yeh, Transparent,
516
Transparent, Transparent, Transparent, Transparent,
518
Transparent, Transparent, Transparent, Transparent,
519
Transparent, Transparent, Transparent, Transparent,
520
Transparent, ArabicNone, ArabicNone, ArabicNone,
521
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
523
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
524
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
525
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
526
ArabicNone, ArabicNone, Beh, Qaf,
528
Transparent, Alef, Alef, Alef,
529
ArabicNone, Alef, Waw, Waw,
541
Reh, Reh, Seen, Seen,
546
Qaf, Gaf, SwashKaf, Gaf,
551
Lam, Noon, Noon, Noon,
552
Noon, Noon, KnottedHeh, Hah,
555
TehMarbuta, HehGoal, HamzaOnHehGoal, HamzaOnHehGoal,
558
Yeh, YehWithTail, Yeh, Waw,
560
Yeh, Yeh, YehBarre, YehBarre,
561
ArabicNone, TehMarbuta, Transparent, Transparent,
562
Transparent, Transparent, Transparent, Transparent,
563
Transparent, ArabicNone, ArabicNone, Transparent,
565
Transparent, Transparent, Transparent, Transparent,
566
Transparent, ArabicNone, ArabicNone, Transparent,
567
Transparent, ArabicNone, Transparent, Transparent,
568
Transparent, Transparent, Dal, Reh,
570
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
571
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
572
ArabicNone, ArabicNone, Seen, Sad,
573
Ain, ArabicNone, ArabicNone, KnottedHeh,
576
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
577
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
578
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
579
ArabicNone, ArabicNone, ArabicNone, ArabicNone,
581
Alaph, Transparent, Beth, Gamal,
582
Gamal, Dalath, Dalath, He,
583
SyriacWaw, Zain, Heth, Teth,
584
Teth, Yudh, YudhHe, Kaph,
586
Lamadh, Mim, Nun, Semakh,
587
FinalSemakh, SyriacE, Pe, ReversedPe,
588
Sadhe, Qaph, Dalath, Shin,
589
Taw, Beth, Gamal, Dalath,
591
Transparent, Transparent, Transparent, Transparent,
592
Transparent, Transparent, Transparent, Transparent,
593
Transparent, Transparent, Transparent, Transparent,
594
Transparent, Transparent, Transparent, Transparent,
596
Transparent, Transparent, Transparent, Transparent,
597
Transparent, Transparent, Transparent, Transparent,
598
Transparent, Transparent, Transparent, ArabicNone,
599
ArabicNone, Zain, Kaph, Fe,
602
static inline ArabicGroup arabicGroup(unsigned short uc)
604
if (uc >= 0x0600 && uc < 0x750)
605
return (ArabicGroup) arabic_group[uc-0x600];
606
else if (uc == 0x200d)
608
else if (::category(uc) == QChar::Separator_Space)
616
Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
619
Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
620
transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
622
Right join-causing: dual + center
623
Left join-causing: dual + right + center
625
Rules are as follows (for a string already in visual order, as we have it here):
627
R1 Transparent characters do not affect joining behaviour.
628
R2 A right joining character, that has a right join-causing char on the right will get form XRight
629
(R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
630
Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
631
R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
632
the right will get form XMedial
633
R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
635
R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
637
R7 Otherwise the character will get form XIsolated
639
Additionally we have to do the minimal ligature support for lam-alef ligatures:
641
L1 Transparent characters do not affect ligature behaviour.
642
L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
643
L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
645
The state table below handles rules R1-R7.
653
// intermediate state
667
static const Joining joining_for_group[ArabicGroupsEnd] = {
670
JNone, // ArabicSpace
672
JTransparent, // Transparent
689
JRight // HamzaOnHehGoal
698
static const JoiningPair joining_table[5][4] =
699
// None, Causing, Dual, Right
701
{ { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XInitial }, { XIsolated, XIsolated } }, // XIsolated
702
{ { XFinal, XIsolated }, { XFinal, XCausing }, { XFinal, XInitial }, { XFinal, XIsolated } }, // XFinal
703
{ { XIsolated, XIsolated }, { XInitial, XCausing }, { XInitial, XMedial }, { XInitial, XFinal } }, // XInitial
704
{ { XFinal, XIsolated }, { XMedial, XCausing }, { XMedial, XMedial }, { XMedial, XFinal } }, // XMedial
705
{ { XIsolated, XIsolated }, { XIsolated, XCausing }, { XIsolated, XMedial }, { XIsolated, XFinal } }, // XCausing
710
According to http://www.microsoft.com/middleeast/Arabicdev/IE6/KBase.asp
712
1. Find the priority of the connecting opportunities in each word
713
2. Add expansion at the highest priority connection opportunity
714
3. If more than one connection opportunity have the same highest value,
715
use the opportunity closest to the end of the word.
717
Following is a chart that provides the priority for connection
718
opportunities and where expansion occurs. The character group names
719
are those in table 6.6 of the UNICODE 2.0 book.
722
PrioritY Glyph Condition Kashida Location
724
Arabic_Kashida User inserted Kashida The user entered a Kashida in a position. After the user
725
(Shift+j or Shift+ļæ½) Thus, it is the highest priority to insert an inserted kashida
728
Arabic_Seen Seen, Sad Connecting to the next character. After the character.
729
(Initial or medial form).
731
Arabic_HaaDal Teh Marbutah, Haa, Dal Connecting to previous character. Before the final form
734
Arabic_Alef Alef, Tah, Lam, Connecting to previous character. Before the final form
735
Kaf and Gaf of these characters.
737
Arabic_BaRa Reh, Yeh Connected to medial Beh Before preceding medial Baa
739
Arabic_Waw Waw, Ain, Qaf, Feh Connecting to previous character. Before the final form of
742
Arabic_Normal Other connecting Connecting to previous character. Before the final form
743
characters of these characters.
747
This seems to imply that we have at most one kashida point per arabic word.
751
struct ArabicProperties {
753
unsigned char justification;
755
Q_DECLARE_TYPEINFO(ArabicProperties, Q_PRIMITIVE_TYPE);
758
static void getArabicProperties(const unsigned short *chars, int len, ArabicProperties *properties)
760
// qDebug("arabicSyriacOpenTypeShape: properties:");
762
int lastGroup = ArabicNone;
764
ArabicGroup group = arabicGroup(chars[0]);
765
Joining j = joining_for_group[group];
766
Shape shape = joining_table[XIsolated][j].form2;
767
properties[0].justification = QGlyphLayout::NoJustification;
769
for (int i = 1; i < len; ++i) {
770
// #### fix handling for spaces and punktuation
771
properties[i].justification = QGlyphLayout::NoJustification;
773
group = arabicGroup(chars[i]);
774
j = joining_for_group[group];
776
if (j == JTransparent) {
777
properties[i].shape = XIsolated;
781
properties[lastPos].shape = joining_table[shape][j].form1;
782
shape = joining_table[shape][j].form2;
786
if (properties[lastPos].shape == XInitial || properties[lastPos].shape == XMedial)
787
properties[i-1].justification = QGlyphLayout::Arabic_Seen;
790
if (properties[lastPos].shape == XFinal)
791
properties[lastPos-1].justification = QGlyphLayout::Arabic_HaaDal;
794
if (properties[lastPos].shape == XFinal)
795
properties[lastPos-1].justification = QGlyphLayout::Arabic_Alef;
798
if (properties[lastPos].shape == XFinal)
799
properties[lastPos-1].justification = QGlyphLayout::Arabic_Waw;
802
if (properties[lastPos].shape == XFinal)
803
properties[lastPos-1].justification = QGlyphLayout::Arabic_Normal;
812
lastGroup = ArabicNone;
817
// ### Center should probably be treated as transparent when it comes to justification.
821
properties[i].justification = QGlyphLayout::Arabic_Space;
824
properties[i].justification = QGlyphLayout::Arabic_Kashida;
842
if (properties[lastPos].shape == XMedial && arabicGroup(chars[lastPos]) == Beh)
843
properties[lastPos-1].justification = QGlyphLayout::Arabic_BaRa;
856
case ArabicGroupsEnd:
862
properties[lastPos].shape = joining_table[shape][JNone].form1;
865
// for (int i = 0; i < len; ++i)
866
// qDebug("arabic properties(%d): uc=%x shape=%d, justification=%d", i, chars[i], properties[i].shape, properties[i].justification);
874
// The unicode to unicode shaping codec.
875
// does only presentation forms B at the moment, but that should be enough for
877
static const ushort arabicUnicodeMapping[256][2] = {
878
// base of shaped forms, and number-1 of them (0 for non shaping,
879
// 1 for right binding and 3 for dual binding
881
// These are just the glyphs available in Unicode,
882
// some characters are in R class, but have no glyphs in Unicode.
884
{ 0x0600, 0 }, // 0x0600
885
{ 0x0601, 0 }, // 0x0601
886
{ 0x0602, 0 }, // 0x0602
887
{ 0x0603, 0 }, // 0x0603
888
{ 0x0604, 0 }, // 0x0604
889
{ 0x0605, 0 }, // 0x0605
890
{ 0x0606, 0 }, // 0x0606
891
{ 0x0607, 0 }, // 0x0607
892
{ 0x0608, 0 }, // 0x0608
893
{ 0x0609, 0 }, // 0x0609
894
{ 0x060A, 0 }, // 0x060A
895
{ 0x060B, 0 }, // 0x060B
896
{ 0x060C, 0 }, // 0x060C
897
{ 0x060D, 0 }, // 0x060D
898
{ 0x060E, 0 }, // 0x060E
899
{ 0x060F, 0 }, // 0x060F
901
{ 0x0610, 0 }, // 0x0610
902
{ 0x0611, 0 }, // 0x0611
903
{ 0x0612, 0 }, // 0x0612
904
{ 0x0613, 0 }, // 0x0613
905
{ 0x0614, 0 }, // 0x0614
906
{ 0x0615, 0 }, // 0x0615
907
{ 0x0616, 0 }, // 0x0616
908
{ 0x0617, 0 }, // 0x0617
909
{ 0x0618, 0 }, // 0x0618
910
{ 0x0619, 0 }, // 0x0619
911
{ 0x061A, 0 }, // 0x061A
912
{ 0x061B, 0 }, // 0x061B
913
{ 0x061C, 0 }, // 0x061C
914
{ 0x061D, 0 }, // 0x061D
915
{ 0x061E, 0 }, // 0x061E
916
{ 0x061F, 0 }, // 0x061F
918
{ 0x0620, 0 }, // 0x0620
919
{ 0xFE80, 0 }, // 0x0621 HAMZA
920
{ 0xFE81, 1 }, // 0x0622 R ALEF WITH MADDA ABOVE
921
{ 0xFE83, 1 }, // 0x0623 R ALEF WITH HAMZA ABOVE
922
{ 0xFE85, 1 }, // 0x0624 R WAW WITH HAMZA ABOVE
923
{ 0xFE87, 1 }, // 0x0625 R ALEF WITH HAMZA BELOW
924
{ 0xFE89, 3 }, // 0x0626 D YEH WITH HAMZA ABOVE
925
{ 0xFE8D, 1 }, // 0x0627 R ALEF
926
{ 0xFE8F, 3 }, // 0x0628 D BEH
927
{ 0xFE93, 1 }, // 0x0629 R TEH MARBUTA
928
{ 0xFE95, 3 }, // 0x062A D TEH
929
{ 0xFE99, 3 }, // 0x062B D THEH
930
{ 0xFE9D, 3 }, // 0x062C D JEEM
931
{ 0xFEA1, 3 }, // 0x062D D HAH
932
{ 0xFEA5, 3 }, // 0x062E D KHAH
933
{ 0xFEA9, 1 }, // 0x062F R DAL
935
{ 0xFEAB, 1 }, // 0x0630 R THAL
936
{ 0xFEAD, 1 }, // 0x0631 R REH
937
{ 0xFEAF, 1 }, // 0x0632 R ZAIN
938
{ 0xFEB1, 3 }, // 0x0633 D SEEN
939
{ 0xFEB5, 3 }, // 0x0634 D SHEEN
940
{ 0xFEB9, 3 }, // 0x0635 D SAD
941
{ 0xFEBD, 3 }, // 0x0636 D DAD
942
{ 0xFEC1, 3 }, // 0x0637 D TAH
943
{ 0xFEC5, 3 }, // 0x0638 D ZAH
944
{ 0xFEC9, 3 }, // 0x0639 D AIN
945
{ 0xFECD, 3 }, // 0x063A D GHAIN
946
{ 0x063B, 0 }, // 0x063B
947
{ 0x063C, 0 }, // 0x063C
948
{ 0x063D, 0 }, // 0x063D
949
{ 0x063E, 0 }, // 0x063E
950
{ 0x063F, 0 }, // 0x063F
952
{ 0x0640, 0 }, // 0x0640 C TATWEEL // ### Join Causing, only one glyph
953
{ 0xFED1, 3 }, // 0x0641 D FEH
954
{ 0xFED5, 3 }, // 0x0642 D QAF
955
{ 0xFED9, 3 }, // 0x0643 D KAF
956
{ 0xFEDD, 3 }, // 0x0644 D LAM
957
{ 0xFEE1, 3 }, // 0x0645 D MEEM
958
{ 0xFEE5, 3 }, // 0x0646 D NOON
959
{ 0xFEE9, 3 }, // 0x0647 D HEH
960
{ 0xFEED, 1 }, // 0x0648 R WAW
961
{ 0x0649, 3 }, // 0x0649 ALEF MAKSURA // ### Dual, glyphs not consecutive, handle in code.
962
{ 0xFEF1, 3 }, // 0x064A D YEH
963
{ 0x064B, 0 }, // 0x064B
964
{ 0x064C, 0 }, // 0x064C
965
{ 0x064D, 0 }, // 0x064D
966
{ 0x064E, 0 }, // 0x064E
967
{ 0x064F, 0 }, // 0x064F
969
{ 0x0650, 0 }, // 0x0650
970
{ 0x0651, 0 }, // 0x0651
971
{ 0x0652, 0 }, // 0x0652
972
{ 0x0653, 0 }, // 0x0653
973
{ 0x0654, 0 }, // 0x0654
974
{ 0x0655, 0 }, // 0x0655
975
{ 0x0656, 0 }, // 0x0656
976
{ 0x0657, 0 }, // 0x0657
977
{ 0x0658, 0 }, // 0x0658
978
{ 0x0659, 0 }, // 0x0659
979
{ 0x065A, 0 }, // 0x065A
980
{ 0x065B, 0 }, // 0x065B
981
{ 0x065C, 0 }, // 0x065C
982
{ 0x065D, 0 }, // 0x065D
983
{ 0x065E, 0 }, // 0x065E
984
{ 0x065F, 0 }, // 0x065F
986
{ 0x0660, 0 }, // 0x0660
987
{ 0x0661, 0 }, // 0x0661
988
{ 0x0662, 0 }, // 0x0662
989
{ 0x0663, 0 }, // 0x0663
990
{ 0x0664, 0 }, // 0x0664
991
{ 0x0665, 0 }, // 0x0665
992
{ 0x0666, 0 }, // 0x0666
993
{ 0x0667, 0 }, // 0x0667
994
{ 0x0668, 0 }, // 0x0668
995
{ 0x0669, 0 }, // 0x0669
996
{ 0x066A, 0 }, // 0x066A
997
{ 0x066B, 0 }, // 0x066B
998
{ 0x066C, 0 }, // 0x066C
999
{ 0x066D, 0 }, // 0x066D
1000
{ 0x066E, 0 }, // 0x066E
1001
{ 0x066F, 0 }, // 0x066F
1003
{ 0x0670, 0 }, // 0x0670
1004
{ 0xFB50, 1 }, // 0x0671 R ALEF WASLA
1005
{ 0x0672, 0 }, // 0x0672
1006
{ 0x0673, 0 }, // 0x0673
1007
{ 0x0674, 0 }, // 0x0674
1008
{ 0x0675, 0 }, // 0x0675
1009
{ 0x0676, 0 }, // 0x0676
1010
{ 0x0677, 0 }, // 0x0677
1011
{ 0x0678, 0 }, // 0x0678
1012
{ 0xFB66, 3 }, // 0x0679 D TTEH
1013
{ 0xFB5E, 3 }, // 0x067A D TTEHEH
1014
{ 0xFB52, 3 }, // 0x067B D BEEH
1015
{ 0x067C, 0 }, // 0x067C
1016
{ 0x067D, 0 }, // 0x067D
1017
{ 0xFB56, 3 }, // 0x067E D PEH
1018
{ 0xFB62, 3 }, // 0x067F D TEHEH
1020
{ 0xFB5A, 3 }, // 0x0680 D BEHEH
1021
{ 0x0681, 0 }, // 0x0681
1022
{ 0x0682, 0 }, // 0x0682
1023
{ 0xFB76, 3 }, // 0x0683 D NYEH
1024
{ 0xFB72, 3 }, // 0x0684 D DYEH
1025
{ 0x0685, 0 }, // 0x0685
1026
{ 0xFB7A, 3 }, // 0x0686 D TCHEH
1027
{ 0xFB7E, 3 }, // 0x0687 D TCHEHEH
1028
{ 0xFB88, 1 }, // 0x0688 R DDAL
1029
{ 0x0689, 0 }, // 0x0689
1030
{ 0x068A, 0 }, // 0x068A
1031
{ 0x068B, 0 }, // 0x068B
1032
{ 0xFB84, 1 }, // 0x068C R DAHAL
1033
{ 0xFB82, 1 }, // 0x068D R DDAHAL
1034
{ 0xFB86, 1 }, // 0x068E R DUL
1035
{ 0x068F, 0 }, // 0x068F
1037
{ 0x0690, 0 }, // 0x0690
1038
{ 0xFB8C, 1 }, // 0x0691 R RREH
1039
{ 0x0692, 0 }, // 0x0692
1040
{ 0x0693, 0 }, // 0x0693
1041
{ 0x0694, 0 }, // 0x0694
1042
{ 0x0695, 0 }, // 0x0695
1043
{ 0x0696, 0 }, // 0x0696
1044
{ 0x0697, 0 }, // 0x0697
1045
{ 0xFB8A, 1 }, // 0x0698 R JEH
1046
{ 0x0699, 0 }, // 0x0699
1047
{ 0x069A, 0 }, // 0x069A
1048
{ 0x069B, 0 }, // 0x069B
1049
{ 0x069C, 0 }, // 0x069C
1050
{ 0x069D, 0 }, // 0x069D
1051
{ 0x069E, 0 }, // 0x069E
1052
{ 0x069F, 0 }, // 0x069F
1054
{ 0x06A0, 0 }, // 0x06A0
1055
{ 0x06A1, 0 }, // 0x06A1
1056
{ 0x06A2, 0 }, // 0x06A2
1057
{ 0x06A3, 0 }, // 0x06A3
1058
{ 0xFB6A, 3 }, // 0x06A4 D VEH
1059
{ 0x06A5, 0 }, // 0x06A5
1060
{ 0xFB6E, 3 }, // 0x06A6 D PEHEH
1061
{ 0x06A7, 0 }, // 0x06A7
1062
{ 0x06A8, 0 }, // 0x06A8
1063
{ 0xFB8E, 3 }, // 0x06A9 D KEHEH
1064
{ 0x06AA, 0 }, // 0x06AA
1065
{ 0x06AB, 0 }, // 0x06AB
1066
{ 0x06AC, 0 }, // 0x06AC
1067
{ 0xFBD3, 3 }, // 0x06AD D NG
1068
{ 0x06AE, 0 }, // 0x06AE
1069
{ 0xFB92, 3 }, // 0x06AF D GAF
1071
{ 0x06B0, 0 }, // 0x06B0
1072
{ 0xFB9A, 3 }, // 0x06B1 D NGOEH
1073
{ 0x06B2, 0 }, // 0x06B2
1074
{ 0xFB96, 3 }, // 0x06B3 D GUEH
1075
{ 0x06B4, 0 }, // 0x06B4
1076
{ 0x06B5, 0 }, // 0x06B5
1077
{ 0x06B6, 0 }, // 0x06B6
1078
{ 0x06B7, 0 }, // 0x06B7
1079
{ 0x06B8, 0 }, // 0x06B8
1080
{ 0x06B9, 0 }, // 0x06B9
1081
{ 0xFB9E, 1 }, // 0x06BA R NOON GHUNNA
1082
{ 0xFBA0, 3 }, // 0x06BB D RNOON
1083
{ 0x06BC, 0 }, // 0x06BC
1084
{ 0x06BD, 0 }, // 0x06BD
1085
{ 0xFBAA, 3 }, // 0x06BE D HEH DOACHASHMEE
1086
{ 0x06BF, 0 }, // 0x06BF
1088
{ 0xFBA4, 1 }, // 0x06C0 R HEH WITH YEH ABOVE
1089
{ 0xFBA6, 3 }, // 0x06C1 D HEH GOAL
1090
{ 0x06C2, 0 }, // 0x06C2
1091
{ 0x06C3, 0 }, // 0x06C3
1092
{ 0x06C4, 0 }, // 0x06C4
1093
{ 0xFBE0, 1 }, // 0x06C5 R KIRGHIZ OE
1094
{ 0xFBD9, 1 }, // 0x06C6 R OE
1095
{ 0xFBD7, 1 }, // 0x06C7 R U
1096
{ 0xFBDB, 1 }, // 0x06C8 R YU
1097
{ 0xFBE2, 1 }, // 0x06C9 R KIRGHIZ YU
1098
{ 0x06CA, 0 }, // 0x06CA
1099
{ 0xFBDE, 1 }, // 0x06CB R VE
1100
{ 0xFBFC, 3 }, // 0x06CC D FARSI YEH
1101
{ 0x06CD, 0 }, // 0x06CD
1102
{ 0x06CE, 0 }, // 0x06CE
1103
{ 0x06CF, 0 }, // 0x06CF
1105
{ 0xFBE4, 3 }, // 0x06D0 D E
1106
{ 0x06D1, 0 }, // 0x06D1
1107
{ 0xFBAE, 1 }, // 0x06D2 R YEH BARREE
1108
{ 0xFBB0, 1 }, // 0x06D3 R YEH BARREE WITH HAMZA ABOVE
1109
{ 0x06D4, 0 }, // 0x06D4
1110
{ 0x06D5, 0 }, // 0x06D5
1111
{ 0x06D6, 0 }, // 0x06D6
1112
{ 0x06D7, 0 }, // 0x06D7
1113
{ 0x06D8, 0 }, // 0x06D8
1114
{ 0x06D9, 0 }, // 0x06D9
1115
{ 0x06DA, 0 }, // 0x06DA
1116
{ 0x06DB, 0 }, // 0x06DB
1117
{ 0x06DC, 0 }, // 0x06DC
1118
{ 0x06DD, 0 }, // 0x06DD
1119
{ 0x06DE, 0 }, // 0x06DE
1120
{ 0x06DF, 0 }, // 0x06DF
1122
{ 0x06E0, 0 }, // 0x06E0
1123
{ 0x06E1, 0 }, // 0x06E1
1124
{ 0x06E2, 0 }, // 0x06E2
1125
{ 0x06E3, 0 }, // 0x06E3
1126
{ 0x06E4, 0 }, // 0x06E4
1127
{ 0x06E5, 0 }, // 0x06E5
1128
{ 0x06E6, 0 }, // 0x06E6
1129
{ 0x06E7, 0 }, // 0x06E7
1130
{ 0x06E8, 0 }, // 0x06E8
1131
{ 0x06E9, 0 }, // 0x06E9
1132
{ 0x06EA, 0 }, // 0x06EA
1133
{ 0x06EB, 0 }, // 0x06EB
1134
{ 0x06EC, 0 }, // 0x06EC
1135
{ 0x06ED, 0 }, // 0x06ED
1136
{ 0x06EE, 0 }, // 0x06EE
1137
{ 0x06EF, 0 }, // 0x06EF
1139
{ 0x06F0, 0 }, // 0x06F0
1140
{ 0x06F1, 0 }, // 0x06F1
1141
{ 0x06F2, 0 }, // 0x06F2
1142
{ 0x06F3, 0 }, // 0x06F3
1143
{ 0x06F4, 0 }, // 0x06F4
1144
{ 0x06F5, 0 }, // 0x06F5
1145
{ 0x06F6, 0 }, // 0x06F6
1146
{ 0x06F7, 0 }, // 0x06F7
1147
{ 0x06F8, 0 }, // 0x06F8
1148
{ 0x06F9, 0 }, // 0x06F9
1149
{ 0x06FA, 0 }, // 0x06FA
1150
{ 0x06FB, 0 }, // 0x06FB
1151
{ 0x06FC, 0 }, // 0x06FC
1152
{ 0x06FD, 0 }, // 0x06FD
1153
{ 0x06FE, 0 }, // 0x06FE
1154
{ 0x06FF, 0 } // 0x06FF
1157
// the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, this table does
1158
static const ushort alefMaksura[4] = {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9};
1160
// this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
1161
// of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
1162
// medial to the final form
1163
static const ushort arabicUnicodeLamAlefMapping[6][4] = {
1164
{ 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, // 0x622 R Alef with Madda above
1165
{ 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, // 0x623 R Alef with Hamza above
1166
{ 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x624 // Just to fill the table ;-)
1167
{ 0xfffd, 0xfffd, 0xfef9, 0xfefa }, // 0x625 R Alef with Hamza below
1168
{ 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x626 // Just to fill the table ;-)
1169
{ 0xfffd, 0xfffd, 0xfefb, 0xfefc } // 0x627 R Alef
1172
static inline int getShape(uchar cell, int shape)
1174
// the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here
1175
uint ch = (cell != 0x49)
1176
? (shape ? arabicUnicodeMapping[cell][0] + shape : 0x600+cell)
1177
: alefMaksura[shape] ;
1183
Two small helper functions for arabic shaping.
1185
static inline const QChar prevChar(const QString *str, int pos)
1187
//qDebug("leftChar: pos=%d", pos);
1189
const QChar *ch = str->unicode() + pos;
1191
if(::category(*ch) != QChar::Mark_NonSpacing)
1196
return QChar::ReplacementCharacter;
1199
static inline const QChar nextChar(const QString *str, int pos)
1202
int len = str->length();
1203
const QChar *ch = str->unicode() + pos;
1205
//qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining());
1206
if(::category(*ch) != QChar::Mark_NonSpacing)
1208
// assume it's a transparent char, this might not be 100% correct
1212
return QChar::ReplacementCharacter;
1216
static void shapedString(const QString *uc, int from, int len, QChar *shapeBuffer, int *shapedLength,
1217
bool reverse, QGlyphLayout *glyphs, unsigned short *logClusters)
1219
Q_ASSERT(uc->length() >= from + len);
1226
QVarLengthArray<ArabicProperties> props(len+2);
1227
ArabicProperties *properties = props.data();
1235
if (f + l < uc->length()) {
1238
getArabicProperties((const unsigned short *)(uc->unicode()+f), l, props.data());
1240
const QChar *ch = uc->unicode() + from;
1241
QChar *data = shapeBuffer;
1242
int clusterStart = 0;
1244
for (int i = 0; i < len; i++) {
1245
uchar r = ch->row();
1246
int gpos = data - shapeBuffer;
1250
uchar c = ch->cell();
1251
if (c == 0x0c || c == 0x0d)
1252
// remove ZWJ and ZWNJ
1256
*data = mirroredChar(*ch);
1260
uchar c = ch->cell();
1262
int shape = properties[i].shape;
1263
// qDebug("mapping U+%x to shape %d glyph=0x%x", ch->unicode(), shape, getShape(c, shape));
1264
// take care of lam-alef ligatures (lam right of alef)
1268
const QChar pch = nextChar(uc, pos);
1269
if (pch.row() == 0x06) {
1270
switch (pch.cell()) {
1275
// qDebug(" lam of lam-alef ligature");
1276
map = arabicUnicodeLamAlefMapping[pch.cell() - 0x22][shape];
1284
case 0x22: // alef with madda
1285
case 0x23: // alef with hamza above
1286
case 0x25: // alef with hamza below
1288
if (prevChar(uc, pos).unicode() == 0x0644) {
1289
// have a lam alef ligature
1290
//qDebug(" alef of lam-alef ligature");
1296
map = getShape(c, shape);
1301
//glyphs[gpos].attributes.zeroWidth = zeroWidth;
1302
if (::category(*ch) == QChar::Mark_NonSpacing) {
1303
glyphs[gpos].attributes.mark = true;
1304
// qDebug("glyph %d (char %d) is mark!", gpos, i);
1306
glyphs[gpos].attributes.mark = false;
1307
clusterStart = data - shapeBuffer;
1309
glyphs[gpos].attributes.clusterStart = !glyphs[gpos].attributes.mark;
1310
glyphs[gpos].attributes.combiningClass = combiningClass(*ch);
1311
glyphs[gpos].attributes.justification = properties[i].justification;
1312
// qDebug("data[%d] = %x (from %x)", gpos, (uint)data->unicode(), ch->unicode());
1316
logClusters[i] = clusterStart;
1318
*shapedLength = data - shapeBuffer;
1321
#if defined(QT_HAVE_FREETYPE)
1323
static bool arabicSyriacOpenTypeShape(QOpenType *openType, QShaperItem *item)
1325
int nglyphs = item->num_glyphs;
1326
if (!item->font->stringToCMap(item->string->unicode()+item->from, item->length, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
1329
heuristicSetGlyphAttributes(item);
1331
QGlyphLayout *glyphs = item->glyphs;
1332
unsigned short *logClusters = item->log_clusters;
1333
const unsigned short *uc = (const unsigned short *)item->string->unicode() + item->from;
1335
QVarLengthArray<ArabicProperties> props(item->length+2);
1336
ArabicProperties *properties = props.data();
1338
int l = item->length;
1339
if (item->from > 0) {
1344
if (f + l < item->string->length()) {
1347
getArabicProperties((const unsigned short *)(uc+f), l, props.data());
1349
QVarLengthArray<bool> apply(item->num_glyphs);
1352
// Hack to remove ZWJ and ZWNJ from rendered output.
1354
for (int i = 0; i < item->num_glyphs; i++) {
1355
if (uc[i] == 0x200c || uc[i] == 0x200d)
1357
glyphs[j] = glyphs[i];
1358
properties[j] = properties[i];
1359
glyphs[j].attributes.justification = properties[i].justification;
1360
logClusters[i] = logClusters[j];
1363
item->num_glyphs = j;
1365
openType->init(item);
1367
// call features in the order defined by http://www.microsoft.com/typography/otfntdev/arabicot/shaping.htm
1368
openType->applyGSUBFeature(FT_MAKE_TAG('c', 'c', 'm', 'p'));
1370
if (item->script == QUnicodeTables::Arabic) {
1375
{ FT_MAKE_TAG('i', 's', 'o', 'l'), XIsolated },
1376
{ FT_MAKE_TAG('f', 'i', 'n', 'a'), XFinal },
1377
{ FT_MAKE_TAG('m', 'e', 'd', 'i'), XMedial },
1378
{ FT_MAKE_TAG('i', 'n', 'i', 't'), XInitial }
1380
for (int j = 0; j < 4; ++j) {
1381
for (int i = 0; i < item->num_glyphs; i++)
1382
apply[i] = (properties[i].shape == features[j].shape);
1383
openType->applyGSUBFeature(features[j].tag, apply.data());
1390
{ FT_MAKE_TAG('i', 's', 'o', 'l'), XIsolated },
1391
{ FT_MAKE_TAG('f', 'i', 'n', 'a'), XFinal },
1392
{ FT_MAKE_TAG('f', 'i', 'n', '2'), XFinal },
1393
{ FT_MAKE_TAG('f', 'i', 'n', '3'), XFinal },
1394
{ FT_MAKE_TAG('m', 'e', 'd', 'i'), XMedial },
1395
{ FT_MAKE_TAG('m', 'e', 'd', '2'), XMedial },
1396
{ FT_MAKE_TAG('i', 'n', 'i', 't'), XInitial }
1398
for (int j = 0; j < 7; ++j) {
1399
for (int i = 0; i < item->num_glyphs; i++)
1400
apply[i] = (properties[i].shape == features[j].shape);
1401
openType->applyGSUBFeature(features[j].tag, apply.data());
1404
const int commonFeatures[] = {
1405
// these features get applied to all glyphs and both scripts
1406
FT_MAKE_TAG('r', 'l', 'i', 'g'),
1407
FT_MAKE_TAG('c', 'a', 'l', 't'),
1408
FT_MAKE_TAG('l', 'i', 'g', 'a'),
1409
FT_MAKE_TAG('d', 'l', 'i', 'g')
1411
for (int j = 0; j < 4; ++j)
1412
openType->applyGSUBFeature(commonFeatures[j]);
1414
if (item->script == QUnicodeTables::Arabic) {
1415
const int features[] = {
1416
FT_MAKE_TAG('c', 's', 'w', 'h'),
1417
// mset is used in old Win95 fonts that don't have a 'mark' positioning table.
1418
FT_MAKE_TAG('m', 's', 'e', 't')
1420
for (int j = 0; j < 2; ++j)
1421
openType->applyGSUBFeature(features[j]);
1424
openType->applyGPOSFeatures();
1426
// reset num_glyphs to what is available.
1427
item->num_glyphs = nglyphs;
1428
return openType->appendTo(item);
1433
static void arabic_attributes(int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes)
1435
const QChar *uc = text.unicode() + from;
1437
for (int i = 0; i < len; i++) {
1438
QChar::Category cat = ::category(*uc);
1439
attributes->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
1440
attributes->softBreak = false;
1441
attributes->charStop = (cat != QChar::Mark_NonSpacing);
1442
attributes->wordStop = false;
1443
attributes->invalid = false;
1450
// #### stil missing: identify invalid character combinations
1451
static bool arabic_shape(QShaperItem *item)
1453
Q_ASSERT(item->script == QUnicodeTables::Arabic);
1455
#ifdef QT_HAVE_FREETYPE
1456
QOpenType *openType = item->font->openType();
1458
if (openType && openType->supportsScript(QUnicodeTables::Arabic))
1459
return arabicSyriacOpenTypeShape(openType, item);
1462
QVarLengthArray<ushort> shapedChars(item->length);
1465
shapedString(item->string, item->from, item->length, (QChar *)shapedChars.data(), &slen,
1466
item->flags & QTextEngine::RightToLeft,
1467
item->glyphs, item->log_clusters);
1469
if (!item->font->stringToCMap((QChar *)shapedChars.data(), slen, item->glyphs, &item->num_glyphs, QFlag(item->flags)))
1472
for (int i = 0; i < slen; ++i)
1473
if (item->glyphs[i].attributes.mark)
1474
item->glyphs[i].advance = QPointF();
1475
qt_heuristicPosition(item);
1479
#if defined(Q_WS_X11) || defined(Q_WS_QWS)
1480
# include "qscriptengine_unix.cpp"
1481
#elif defined(Q_WS_WIN)
1482
# include "qscriptengine_win.cpp"
1483
#elif defined(Q_WS_MAC)
1484
# include "qscriptengine_mac.cpp"