1 | /****************************************************************************
|
---|
2 | ** $Id: qscriptengine.cpp 8 2005-11-16 19:36:46Z dmik $
|
---|
3 | **
|
---|
4 | ** ???
|
---|
5 | **
|
---|
6 | ** Copyright (C) 2003 Trolltech AS. All rights reserved.
|
---|
7 | **
|
---|
8 | ** This file is part of the kernel module of the Qt GUI Toolkit.
|
---|
9 | **
|
---|
10 | ** This file may be distributed under the terms of the Q Public License
|
---|
11 | ** as defined by Trolltech AS of Norway and appearing in the file
|
---|
12 | ** LICENSE.QPL included in the packaging of this file.
|
---|
13 | **
|
---|
14 | ** This file may be distributed and/or modified under the terms of the
|
---|
15 | ** GNU General Public License version 2 as published by the Free Software
|
---|
16 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
17 | ** packaging of this file.
|
---|
18 | **
|
---|
19 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
20 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
21 | ** Agreement provided with the Software.
|
---|
22 | **
|
---|
23 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
24 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
25 | **
|
---|
26 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
27 | ** information about Qt Commercial License Agreements.
|
---|
28 | ** See http://www.trolltech.com/qpl/ for QPL licensing information.
|
---|
29 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
30 | **
|
---|
31 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
32 | ** not clear to you.
|
---|
33 | **
|
---|
34 | **********************************************************************/
|
---|
35 |
|
---|
36 | #include "qscriptengine_p.h"
|
---|
37 |
|
---|
38 | #include "qstring.h"
|
---|
39 | #include "qrect.h"
|
---|
40 | #include "qfont.h"
|
---|
41 | #include <private/qunicodetables_p.h>
|
---|
42 | #include "qtextengine_p.h"
|
---|
43 | #include "qfontengine_p.h"
|
---|
44 | #include <stdlib.h>
|
---|
45 |
|
---|
46 |
|
---|
47 | #undef None
|
---|
48 | #undef Pre
|
---|
49 | #undef Above
|
---|
50 | #undef Below
|
---|
51 |
|
---|
52 | // --------------------------------------------------------------------------------------------------------------------------------------------
|
---|
53 | //
|
---|
54 | // Basic processing
|
---|
55 | //
|
---|
56 | // --------------------------------------------------------------------------------------------------------------------------------------------
|
---|
57 |
|
---|
58 | static inline void positionCluster( QTextEngine *engine, QScriptItem *si, int gfrom, int glast )
|
---|
59 | {
|
---|
60 | si->hasPositioning = TRUE;
|
---|
61 | int nmarks = glast - gfrom;
|
---|
62 | if ( nmarks <= 0 ) {
|
---|
63 | qWarning( "positionCluster: no marks to position!" );
|
---|
64 | return;
|
---|
65 | }
|
---|
66 |
|
---|
67 | glyph_t *glyphs = engine->glyphs( si );
|
---|
68 | qoffset_t *offsets = engine->offsets( si );
|
---|
69 | GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
|
---|
70 |
|
---|
71 | QFontEngine *f = si->fontEngine;
|
---|
72 | glyph_metrics_t baseInfo = f->boundingBox( glyphs[gfrom] );
|
---|
73 |
|
---|
74 | if ( si->analysis.script == QFont::Hebrew ) {
|
---|
75 | // we need to attach below the baseline, because of the hebrew iud.
|
---|
76 | baseInfo.height= QMAX( baseInfo.height, -baseInfo.y );
|
---|
77 | }
|
---|
78 | QRect baseRect( baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height );
|
---|
79 |
|
---|
80 | // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast );
|
---|
81 | // qDebug( "baseInfo: %d/%d (%d/%d) off=%d/%d", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff );
|
---|
82 |
|
---|
83 | int size = f->ascent()/10;
|
---|
84 | int offsetBase = (size - 4) / 4 + QMIN( size, 4 ) + 1;
|
---|
85 | // qDebug("offset = %d", offsetBase );
|
---|
86 |
|
---|
87 | bool rightToLeft = si->analysis.bidiLevel % 2;
|
---|
88 |
|
---|
89 | int i;
|
---|
90 | int lastCmb = -1;
|
---|
91 | QRect attachmentRect;
|
---|
92 |
|
---|
93 | for( i = 1; i <= nmarks; i++ ) {
|
---|
94 | glyph_t mark = glyphs[gfrom+i];
|
---|
95 | QPoint p;
|
---|
96 | glyph_metrics_t markInfo = f->boundingBox( mark );
|
---|
97 | QRect markRect( markInfo.x, markInfo.y, markInfo.width, markInfo.height );
|
---|
98 |
|
---|
99 | int offset = offsetBase;
|
---|
100 | unsigned char cmb = glyphAttributes[gfrom+i].combiningClass;
|
---|
101 |
|
---|
102 | // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
|
---|
103 | // bits in the glyphAttributes structure.
|
---|
104 | if ( cmb < 200 ) {
|
---|
105 | // fixed position classes. We approximate by mapping to one of the others.
|
---|
106 | // currently I added only the ones for arabic, hebrew, lao and thai.
|
---|
107 |
|
---|
108 | // for Lao and Thai marks with class 0, see below ( heuristicSetGlyphAttributes )
|
---|
109 |
|
---|
110 | // add a bit more offset to arabic, a bit hacky
|
---|
111 | if ( cmb >= 27 && cmb <= 36 && offset < 3 )
|
---|
112 | offset +=1;
|
---|
113 | // below
|
---|
114 | if ( (cmb >= 10 && cmb <= 18) ||
|
---|
115 | cmb == 20 || cmb == 22 ||
|
---|
116 | cmb == 29 || cmb == 32 )
|
---|
117 | cmb = QChar::Combining_Below;
|
---|
118 | // above
|
---|
119 | else if ( cmb == 23 || cmb == 27 || cmb == 28 ||
|
---|
120 | cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36 ) )
|
---|
121 | cmb = QChar::Combining_Above;
|
---|
122 | //below-right
|
---|
123 | else if ( cmb == 9 || cmb == 103 || cmb == 118 )
|
---|
124 | cmb = QChar::Combining_BelowRight;
|
---|
125 | // above-right
|
---|
126 | else if ( cmb == 24 || cmb == 107 || cmb == 122 )
|
---|
127 | cmb = QChar::Combining_AboveRight;
|
---|
128 | else if ( cmb == 25 )
|
---|
129 | cmb = QChar::Combining_AboveLeft;
|
---|
130 | // fixed:
|
---|
131 | // 19 21
|
---|
132 |
|
---|
133 | }
|
---|
134 |
|
---|
135 | // combining marks of different class don't interact. Reset the rectangle.
|
---|
136 | if ( cmb != lastCmb ) {
|
---|
137 | //qDebug( "resetting rect" );
|
---|
138 | attachmentRect = baseRect;
|
---|
139 | }
|
---|
140 |
|
---|
141 | switch( cmb ) {
|
---|
142 | case QChar::Combining_DoubleBelow:
|
---|
143 | // ### wrong in rtl context!
|
---|
144 | case QChar::Combining_BelowLeft:
|
---|
145 | p += QPoint( 0, offset );
|
---|
146 | case QChar::Combining_BelowLeftAttached:
|
---|
147 | p += attachmentRect.bottomLeft() - markRect.topLeft();
|
---|
148 | break;
|
---|
149 | case QChar::Combining_Below:
|
---|
150 | p += QPoint( 0, offset );
|
---|
151 | case QChar::Combining_BelowAttached:
|
---|
152 | p += attachmentRect.bottomLeft() - markRect.topLeft();
|
---|
153 | p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
|
---|
154 | break;
|
---|
155 | case QChar::Combining_BelowRight:
|
---|
156 | p += QPoint( 0, offset );
|
---|
157 | case QChar::Combining_BelowRightAttached:
|
---|
158 | p += attachmentRect.bottomRight() - markRect.topRight();
|
---|
159 | break;
|
---|
160 | case QChar::Combining_Left:
|
---|
161 | p += QPoint( -offset, 0 );
|
---|
162 | case QChar::Combining_LeftAttached:
|
---|
163 | break;
|
---|
164 | case QChar::Combining_Right:
|
---|
165 | p += QPoint( offset, 0 );
|
---|
166 | case QChar::Combining_RightAttached:
|
---|
167 | break;
|
---|
168 | case QChar::Combining_DoubleAbove:
|
---|
169 | // ### wrong in RTL context!
|
---|
170 | case QChar::Combining_AboveLeft:
|
---|
171 | p += QPoint( 0, -offset );
|
---|
172 | case QChar::Combining_AboveLeftAttached:
|
---|
173 | p += attachmentRect.topLeft() - markRect.bottomLeft();
|
---|
174 | break;
|
---|
175 | case QChar::Combining_Above:
|
---|
176 | p += QPoint( 0, -offset );
|
---|
177 | case QChar::Combining_AboveAttached:
|
---|
178 | p += attachmentRect.topLeft() - markRect.bottomLeft();
|
---|
179 | p += QPoint( (attachmentRect.width() - markRect.width())/2 , 0 );
|
---|
180 | break;
|
---|
181 | case QChar::Combining_AboveRight:
|
---|
182 | p += QPoint( 0, -offset );
|
---|
183 | case QChar::Combining_AboveRightAttached:
|
---|
184 | p += attachmentRect.topRight() - markRect.bottomRight();
|
---|
185 | break;
|
---|
186 |
|
---|
187 | case QChar::Combining_IotaSubscript:
|
---|
188 | default:
|
---|
189 | break;
|
---|
190 | }
|
---|
191 | // qDebug( "char=%x combiningClass = %d offset=%d/%d", mark, cmb, p.x(), p.y() );
|
---|
192 | markRect.moveBy( p.x(), p.y() );
|
---|
193 | attachmentRect |= markRect;
|
---|
194 | lastCmb = cmb;
|
---|
195 | if ( rightToLeft ) {
|
---|
196 | offsets[gfrom+i].x = p.x();
|
---|
197 | offsets[gfrom+i].y = p.y() - baseInfo.yoff;
|
---|
198 | } else {
|
---|
199 | offsets[gfrom+i].x = p.x() - baseInfo.xoff;
|
---|
200 | offsets[gfrom+i].y = p.y() - baseInfo.yoff;
|
---|
201 | }
|
---|
202 | }
|
---|
203 | }
|
---|
204 |
|
---|
205 |
|
---|
206 | void q_heuristicPosition( QTextEngine *engine, QScriptItem *si )
|
---|
207 | {
|
---|
208 | GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
|
---|
209 |
|
---|
210 | int cEnd = -1;
|
---|
211 | int i = si->num_glyphs;
|
---|
212 | while ( i-- ) {
|
---|
213 | if ( cEnd == -1 && glyphAttributes[i].mark ) {
|
---|
214 | cEnd = i;
|
---|
215 | } else if ( cEnd != -1 && !glyphAttributes[i].mark ) {
|
---|
216 | positionCluster( engine, si, i, cEnd );
|
---|
217 | cEnd = -1;
|
---|
218 | }
|
---|
219 | }
|
---|
220 | }
|
---|
221 |
|
---|
222 |
|
---|
223 |
|
---|
224 | // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars ang glyphs
|
---|
225 | // and no reordering.
|
---|
226 | // also computes logClusters heuristically
|
---|
227 | static void heuristicSetGlyphAttributes( const QString &string, int from, int len,
|
---|
228 | QTextEngine *engine, QScriptItem *si )
|
---|
229 | {
|
---|
230 | // ### zeroWidth and justification are missing here!!!!!
|
---|
231 |
|
---|
232 | if ( si->num_glyphs != len )
|
---|
233 | qWarning("QScriptEngine::heuristicSetGlyphAttributes: char length and num glyphs disagree" );
|
---|
234 |
|
---|
235 | // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", num_glyphs);
|
---|
236 | GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
|
---|
237 | unsigned short *logClusters = engine->logClusters( si );
|
---|
238 | advance_t *advances = engine->advances( si );
|
---|
239 |
|
---|
240 | // honour the logClusters array if it exists.
|
---|
241 | const QChar *uc = string.unicode() + from;
|
---|
242 |
|
---|
243 | for ( int i = 0; i < si->num_glyphs; i++ )
|
---|
244 | logClusters[i] = i;
|
---|
245 |
|
---|
246 | int pos = 1;
|
---|
247 |
|
---|
248 | // first char in a run is never (treated as) a mark
|
---|
249 | int cStart = 0;
|
---|
250 | glyphAttributes[0].mark = FALSE;
|
---|
251 | glyphAttributes[0].clusterStart = TRUE;
|
---|
252 |
|
---|
253 | while ( pos < len ) {
|
---|
254 | if ( ::category( uc[pos] ) != QChar::Mark_NonSpacing ) {
|
---|
255 | glyphAttributes[pos].mark = FALSE;
|
---|
256 | glyphAttributes[pos].clusterStart = TRUE;
|
---|
257 | glyphAttributes[pos].combiningClass = 0;
|
---|
258 | cStart = pos;
|
---|
259 | } else {
|
---|
260 | int cmb = combiningClass( uc[pos] );
|
---|
261 |
|
---|
262 | if ( cmb == 0 ) {
|
---|
263 | // Fix 0 combining classes
|
---|
264 | if ( uc[pos].row() == 0x0e ) {
|
---|
265 | // thai or lao
|
---|
266 | unsigned char col = uc[pos].cell();
|
---|
267 | if ( col == 0x31 ||
|
---|
268 | col == 0x34 ||
|
---|
269 | col == 0x35 ||
|
---|
270 | col == 0x36 ||
|
---|
271 | col == 0x37 ||
|
---|
272 | col == 0x47 ||
|
---|
273 | col == 0x4c ||
|
---|
274 | col == 0x4d ||
|
---|
275 | col == 0x4e ) {
|
---|
276 | cmb = QChar::Combining_AboveRight;
|
---|
277 | } else if ( col == 0xb1 ||
|
---|
278 | col == 0xb4 ||
|
---|
279 | col == 0xb5 ||
|
---|
280 | col == 0xb6 ||
|
---|
281 | col == 0xb7 ||
|
---|
282 | col == 0xbb ||
|
---|
283 | col == 0xcc ||
|
---|
284 | col == 0xcd ) {
|
---|
285 | cmb = QChar::Combining_Above;
|
---|
286 | } else if ( col == 0xbc ) {
|
---|
287 | cmb = QChar::Combining_Below;
|
---|
288 | }
|
---|
289 | }
|
---|
290 | }
|
---|
291 |
|
---|
292 | glyphAttributes[pos].mark = TRUE;
|
---|
293 | glyphAttributes[pos].clusterStart = FALSE;
|
---|
294 | glyphAttributes[pos].combiningClass = cmb;
|
---|
295 | // qDebug("found a mark at position %d", pos );
|
---|
296 | logClusters[pos] = cStart;
|
---|
297 | advances[pos] = 0;
|
---|
298 | si->hasPositioning = TRUE;
|
---|
299 | }
|
---|
300 | pos++;
|
---|
301 | }
|
---|
302 | }
|
---|
303 |
|
---|
304 | static void convertToCMap( const QChar *chars, int len, QTextEngine *engine, QScriptItem *si )
|
---|
305 | {
|
---|
306 | glyph_t *glyphs = engine->glyphs( si );
|
---|
307 | advance_t *advances = engine->advances( si );
|
---|
308 |
|
---|
309 | si->num_glyphs = len;
|
---|
310 | engine->ensureSpace( len );
|
---|
311 | int error = si->fontEngine->stringToCMap( chars, len, glyphs, advances, &si->num_glyphs, (si->analysis.bidiLevel %2) );
|
---|
312 | if ( error == QFontEngine::OutOfMemory ) {
|
---|
313 | engine->ensureSpace( si->num_glyphs );
|
---|
314 | si->fontEngine->stringToCMap( chars, len, glyphs, advances, &si->num_glyphs, (si->analysis.bidiLevel %2) );
|
---|
315 | }
|
---|
316 | }
|
---|
317 |
|
---|
318 | static void basic_shape( int /*script*/, const QString &string, int from, int len, QTextEngine *engine, QScriptItem *si )
|
---|
319 | {
|
---|
320 | convertToCMap( string.unicode() + from, len, engine, si );
|
---|
321 | heuristicSetGlyphAttributes( string, from, len, engine, si );
|
---|
322 | if ( !engine->widthOnly ) {
|
---|
323 | q_heuristicPosition( engine, si );
|
---|
324 | }
|
---|
325 | }
|
---|
326 |
|
---|
327 | static void basic_attributes( int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes )
|
---|
328 | {
|
---|
329 | const QChar *uc = text.unicode() + from;
|
---|
330 | attributes += from;
|
---|
331 |
|
---|
332 | QCharAttributes *a = attributes;
|
---|
333 |
|
---|
334 | for ( int i = 0; i < len; i++ ) {
|
---|
335 | QChar::Category cat = ::category( *uc );
|
---|
336 | a->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
|
---|
337 | a->softBreak = FALSE;
|
---|
338 | a->charStop = (cat != QChar::Mark_NonSpacing);
|
---|
339 | a->wordStop = FALSE;
|
---|
340 | a->invalid = FALSE;
|
---|
341 | ++uc;
|
---|
342 | ++a;
|
---|
343 | }
|
---|
344 | }
|
---|
345 |
|
---|
346 | // --------------------------------------------------------------------------------------------------------------------------------------------
|
---|
347 | //
|
---|
348 | // Middle eastern languages
|
---|
349 | //
|
---|
350 | // --------------------------------------------------------------------------------------------------------------------------------------------
|
---|
351 |
|
---|
352 | /*
|
---|
353 | Arabic shaping obeys a number of rules according to the joining classes (see Unicode book, section on
|
---|
354 | arabic).
|
---|
355 |
|
---|
356 | Each unicode char has a joining class (right, dual (left&right), center (joincausing) or transparent).
|
---|
357 | transparent joining is not encoded in QChar::joining(), but applies to all combining marks and format marks.
|
---|
358 |
|
---|
359 | Right join-causing: dual + center
|
---|
360 | Left join-causing: dual + right + center
|
---|
361 |
|
---|
362 | Rules are as follows (for a string already in visual order, as we have it here):
|
---|
363 |
|
---|
364 | R1 Transparent characters do not affect joining behaviour.
|
---|
365 | R2 A right joining character, that has a right join-causing char on the right will get form XRight
|
---|
366 | (R3 A left joining character, that has a left join-causing char on the left will get form XLeft)
|
---|
367 | Note: the above rule is meaningless, as there are no pure left joining characters defined in Unicode
|
---|
368 | R4 A dual joining character, that has a left join-causing char on the left and a right join-causing char on
|
---|
369 | the right will get form XMedial
|
---|
370 | R5 A dual joining character, that has a right join causing char on the right, and no left join causing char on the left
|
---|
371 | will get form XRight
|
---|
372 | R6 A dual joining character, that has a left join causing char on the left, and no right join causing char on the right
|
---|
373 | will get form XLeft
|
---|
374 | R7 Otherwise the character will get form XIsolated
|
---|
375 |
|
---|
376 | Additionally we have to do the minimal ligature support for lam-alef ligatures:
|
---|
377 |
|
---|
378 | L1 Transparent characters do not affect ligature behaviour.
|
---|
379 | L2 Any sequence of Alef(XRight) + Lam(XMedial) will form the ligature Alef.Lam(XLeft)
|
---|
380 | L3 Any sequence of Alef(XRight) + Lam(XLeft) will form the ligature Alef.Lam(XIsolated)
|
---|
381 |
|
---|
382 | The two functions defined in this class do shaping in visual and logical order. For logical order just replace right with
|
---|
383 | previous and left with next in the above rules ;-)
|
---|
384 | */
|
---|
385 |
|
---|
386 | /*
|
---|
387 | Two small helper functions for arabic shaping. They get the next shape causing character on either
|
---|
388 | side of the char in question. Implements rule R1.
|
---|
389 |
|
---|
390 | leftChar() returns true if the char to the left is a left join-causing char
|
---|
391 | rightChar() returns true if the char to the right is a right join-causing char
|
---|
392 | */
|
---|
393 |
|
---|
394 |
|
---|
395 | enum Shape {
|
---|
396 | XIsolated,
|
---|
397 | XFinal,
|
---|
398 | XInitial,
|
---|
399 | XMedial
|
---|
400 | };
|
---|
401 |
|
---|
402 | /*
|
---|
403 | Two small helper functions for arabic shaping. They get the next shape causing character on either
|
---|
404 | side of the char in question. Implements rule R1.
|
---|
405 |
|
---|
406 | leftChar() returns true if the char to the left is a left join-causing char
|
---|
407 | rightChar() returns true if the char to the right is a right join-causing char
|
---|
408 | */
|
---|
409 | static inline const QChar *prevChar( const QString &str, int pos )
|
---|
410 | {
|
---|
411 | //qDebug("leftChar: pos=%d", pos);
|
---|
412 | pos--;
|
---|
413 | const QChar *ch = str.unicode() + pos;
|
---|
414 | while( pos > -1 ) {
|
---|
415 | if( ::category( *ch ) != QChar::Mark_NonSpacing )
|
---|
416 | return ch;
|
---|
417 | pos--;
|
---|
418 | ch--;
|
---|
419 | }
|
---|
420 | return &QChar::replacement;
|
---|
421 | }
|
---|
422 |
|
---|
423 | static inline const QChar *nextChar( const QString &str, int pos)
|
---|
424 | {
|
---|
425 | pos++;
|
---|
426 | int len = str.length();
|
---|
427 | const QChar *ch = str.unicode() + pos;
|
---|
428 | while( pos < len ) {
|
---|
429 | //qDebug("rightChar: %d isLetter=%d, joining=%d", pos, ch.isLetter(), ch.joining());
|
---|
430 | if( ::category( *ch ) != QChar::Mark_NonSpacing )
|
---|
431 | return ch;
|
---|
432 | // assume it's a transparent char, this might not be 100% correct
|
---|
433 | pos++;
|
---|
434 | ch++;
|
---|
435 | }
|
---|
436 | return &QChar::replacement;
|
---|
437 | }
|
---|
438 |
|
---|
439 | /* and the same thing for logical ordering :)
|
---|
440 | */
|
---|
441 | static inline bool prevLogicalCharJoins( const QString &str, int pos)
|
---|
442 | {
|
---|
443 | return ( joining( *nextChar( str, pos ) ) != QChar::OtherJoining );
|
---|
444 | }
|
---|
445 |
|
---|
446 | static inline bool nextLogicalCharJoins( const QString &str, int pos)
|
---|
447 | {
|
---|
448 | QChar::Joining join = joining( *prevChar( str, pos ) );
|
---|
449 | return ( join == QChar::Dual || join == QChar::Center );
|
---|
450 | }
|
---|
451 |
|
---|
452 |
|
---|
453 | static inline Shape glyphVariantLogical( const QString &str, int pos)
|
---|
454 | {
|
---|
455 | QChar::Joining joining = ::joining( str.unicode()[pos] );
|
---|
456 | //qDebug("checking %x, joining=%d", str[pos].unicode(), joining);
|
---|
457 | switch ( joining ) {
|
---|
458 | case QChar::OtherJoining:
|
---|
459 | case QChar::Center:
|
---|
460 | // these don't change shape
|
---|
461 | return XIsolated;
|
---|
462 | case QChar::Right:
|
---|
463 | // only rule R2 applies
|
---|
464 | return ( nextLogicalCharJoins( str, pos ) ) ? XFinal : XIsolated;
|
---|
465 | case QChar::Dual:
|
---|
466 | bool right = nextLogicalCharJoins( str, pos );
|
---|
467 | bool left = prevLogicalCharJoins( str, pos );
|
---|
468 | //qDebug("dual: right=%d, left=%d", right, left);
|
---|
469 | return ( right ) ? ( left ? XMedial : XFinal ) : ( left ? XInitial : XIsolated );
|
---|
470 | }
|
---|
471 | return XIsolated;
|
---|
472 | }
|
---|
473 |
|
---|
474 |
|
---|
475 | // The unicode to unicode shaping codec.
|
---|
476 | // does only presentation forms B at the moment, but that should be enough for
|
---|
477 | // simple display
|
---|
478 | static const ushort arabicUnicodeMapping[256][2] = {
|
---|
479 | // base of shaped forms, and number-1 of them ( 0 for non shaping,
|
---|
480 | // 1 for right binding and 3 for dual binding
|
---|
481 |
|
---|
482 | // These are just the glyphs available in Unicode,
|
---|
483 | // some characters are in R class, but have no glyphs in Unicode.
|
---|
484 |
|
---|
485 | { 0x0600, 0 }, // 0x0600
|
---|
486 | { 0x0601, 0 }, // 0x0601
|
---|
487 | { 0x0602, 0 }, // 0x0602
|
---|
488 | { 0x0603, 0 }, // 0x0603
|
---|
489 | { 0x0604, 0 }, // 0x0604
|
---|
490 | { 0x0605, 0 }, // 0x0605
|
---|
491 | { 0x0606, 0 }, // 0x0606
|
---|
492 | { 0x0607, 0 }, // 0x0607
|
---|
493 | { 0x0608, 0 }, // 0x0608
|
---|
494 | { 0x0609, 0 }, // 0x0609
|
---|
495 | { 0x060A, 0 }, // 0x060A
|
---|
496 | { 0x060B, 0 }, // 0x060B
|
---|
497 | { 0x060C, 0 }, // 0x060C
|
---|
498 | { 0x060D, 0 }, // 0x060D
|
---|
499 | { 0x060E, 0 }, // 0x060E
|
---|
500 | { 0x060F, 0 }, // 0x060F
|
---|
501 |
|
---|
502 | { 0x0610, 0 }, // 0x0610
|
---|
503 | { 0x0611, 0 }, // 0x0611
|
---|
504 | { 0x0612, 0 }, // 0x0612
|
---|
505 | { 0x0613, 0 }, // 0x0613
|
---|
506 | { 0x0614, 0 }, // 0x0614
|
---|
507 | { 0x0615, 0 }, // 0x0615
|
---|
508 | { 0x0616, 0 }, // 0x0616
|
---|
509 | { 0x0617, 0 }, // 0x0617
|
---|
510 | { 0x0618, 0 }, // 0x0618
|
---|
511 | { 0x0619, 0 }, // 0x0619
|
---|
512 | { 0x061A, 0 }, // 0x061A
|
---|
513 | { 0x061B, 0 }, // 0x061B
|
---|
514 | { 0x061C, 0 }, // 0x061C
|
---|
515 | { 0x061D, 0 }, // 0x061D
|
---|
516 | { 0x061E, 0 }, // 0x061E
|
---|
517 | { 0x061F, 0 }, // 0x061F
|
---|
518 |
|
---|
519 | { 0x0620, 0 }, // 0x0620
|
---|
520 | { 0xFE80, 0 }, // 0x0621 HAMZA
|
---|
521 | { 0xFE81, 1 }, // 0x0622 R ALEF WITH MADDA ABOVE
|
---|
522 | { 0xFE83, 1 }, // 0x0623 R ALEF WITH HAMZA ABOVE
|
---|
523 | { 0xFE85, 1 }, // 0x0624 R WAW WITH HAMZA ABOVE
|
---|
524 | { 0xFE87, 1 }, // 0x0625 R ALEF WITH HAMZA BELOW
|
---|
525 | { 0xFE89, 3 }, // 0x0626 D YEH WITH HAMZA ABOVE
|
---|
526 | { 0xFE8D, 1 }, // 0x0627 R ALEF
|
---|
527 | { 0xFE8F, 3 }, // 0x0628 D BEH
|
---|
528 | { 0xFE93, 1 }, // 0x0629 R TEH MARBUTA
|
---|
529 | { 0xFE95, 3 }, // 0x062A D TEH
|
---|
530 | { 0xFE99, 3 }, // 0x062B D THEH
|
---|
531 | { 0xFE9D, 3 }, // 0x062C D JEEM
|
---|
532 | { 0xFEA1, 3 }, // 0x062D D HAH
|
---|
533 | { 0xFEA5, 3 }, // 0x062E D KHAH
|
---|
534 | { 0xFEA9, 1 }, // 0x062F R DAL
|
---|
535 |
|
---|
536 | { 0xFEAB, 1 }, // 0x0630 R THAL
|
---|
537 | { 0xFEAD, 1 }, // 0x0631 R REH
|
---|
538 | { 0xFEAF, 1 }, // 0x0632 R ZAIN
|
---|
539 | { 0xFEB1, 3 }, // 0x0633 D SEEN
|
---|
540 | { 0xFEB5, 3 }, // 0x0634 D SHEEN
|
---|
541 | { 0xFEB9, 3 }, // 0x0635 D SAD
|
---|
542 | { 0xFEBD, 3 }, // 0x0636 D DAD
|
---|
543 | { 0xFEC1, 3 }, // 0x0637 D TAH
|
---|
544 | { 0xFEC5, 3 }, // 0x0638 D ZAH
|
---|
545 | { 0xFEC9, 3 }, // 0x0639 D AIN
|
---|
546 | { 0xFECD, 3 }, // 0x063A D GHAIN
|
---|
547 | { 0x063B, 0 }, // 0x063B
|
---|
548 | { 0x063C, 0 }, // 0x063C
|
---|
549 | { 0x063D, 0 }, // 0x063D
|
---|
550 | { 0x063E, 0 }, // 0x063E
|
---|
551 | { 0x063F, 0 }, // 0x063F
|
---|
552 |
|
---|
553 | { 0x0640, 0 }, // 0x0640 C TATWEEL // ### Join Causing, only one glyph
|
---|
554 | { 0xFED1, 3 }, // 0x0641 D FEH
|
---|
555 | { 0xFED5, 3 }, // 0x0642 D QAF
|
---|
556 | { 0xFED9, 3 }, // 0x0643 D KAF
|
---|
557 | { 0xFEDD, 3 }, // 0x0644 D LAM
|
---|
558 | { 0xFEE1, 3 }, // 0x0645 D MEEM
|
---|
559 | { 0xFEE5, 3 }, // 0x0646 D NOON
|
---|
560 | { 0xFEE9, 3 }, // 0x0647 D HEH
|
---|
561 | { 0xFEED, 1 }, // 0x0648 R WAW
|
---|
562 | { 0x0649, 0 }, // 0x0649 ALEF MAKSURA // ### Dual, glyphs not consecutive, handle in code.
|
---|
563 | { 0xFEF1, 3 }, // 0x064A D YEH
|
---|
564 | { 0x064B, 0 }, // 0x064B
|
---|
565 | { 0x064C, 0 }, // 0x064C
|
---|
566 | { 0x064D, 0 }, // 0x064D
|
---|
567 | { 0x064E, 0 }, // 0x064E
|
---|
568 | { 0x064F, 0 }, // 0x064F
|
---|
569 |
|
---|
570 | { 0x0650, 0 }, // 0x0650
|
---|
571 | { 0x0651, 0 }, // 0x0651
|
---|
572 | { 0x0652, 0 }, // 0x0652
|
---|
573 | { 0x0653, 0 }, // 0x0653
|
---|
574 | { 0x0654, 0 }, // 0x0654
|
---|
575 | { 0x0655, 0 }, // 0x0655
|
---|
576 | { 0x0656, 0 }, // 0x0656
|
---|
577 | { 0x0657, 0 }, // 0x0657
|
---|
578 | { 0x0658, 0 }, // 0x0658
|
---|
579 | { 0x0659, 0 }, // 0x0659
|
---|
580 | { 0x065A, 0 }, // 0x065A
|
---|
581 | { 0x065B, 0 }, // 0x065B
|
---|
582 | { 0x065C, 0 }, // 0x065C
|
---|
583 | { 0x065D, 0 }, // 0x065D
|
---|
584 | { 0x065E, 0 }, // 0x065E
|
---|
585 | { 0x065F, 0 }, // 0x065F
|
---|
586 |
|
---|
587 | { 0x0660, 0 }, // 0x0660
|
---|
588 | { 0x0661, 0 }, // 0x0661
|
---|
589 | { 0x0662, 0 }, // 0x0662
|
---|
590 | { 0x0663, 0 }, // 0x0663
|
---|
591 | { 0x0664, 0 }, // 0x0664
|
---|
592 | { 0x0665, 0 }, // 0x0665
|
---|
593 | { 0x0666, 0 }, // 0x0666
|
---|
594 | { 0x0667, 0 }, // 0x0667
|
---|
595 | { 0x0668, 0 }, // 0x0668
|
---|
596 | { 0x0669, 0 }, // 0x0669
|
---|
597 | { 0x066A, 0 }, // 0x066A
|
---|
598 | { 0x066B, 0 }, // 0x066B
|
---|
599 | { 0x066C, 0 }, // 0x066C
|
---|
600 | { 0x066D, 0 }, // 0x066D
|
---|
601 | { 0x066E, 0 }, // 0x066E
|
---|
602 | { 0x066F, 0 }, // 0x066F
|
---|
603 |
|
---|
604 | { 0x0670, 0 }, // 0x0670
|
---|
605 | { 0xFB50, 1 }, // 0x0671 R ALEF WASLA
|
---|
606 | { 0x0672, 0 }, // 0x0672
|
---|
607 | { 0x0673, 0 }, // 0x0673
|
---|
608 | { 0x0674, 0 }, // 0x0674
|
---|
609 | { 0x0675, 0 }, // 0x0675
|
---|
610 | { 0x0676, 0 }, // 0x0676
|
---|
611 | { 0x0677, 0 }, // 0x0677
|
---|
612 | { 0x0678, 0 }, // 0x0678
|
---|
613 | { 0xFB66, 3 }, // 0x0679 D TTEH
|
---|
614 | { 0xFB5E, 3 }, // 0x067A D TTEHEH
|
---|
615 | { 0xFB52, 3 }, // 0x067B D BEEH
|
---|
616 | { 0x067C, 0 }, // 0x067C
|
---|
617 | { 0x067D, 0 }, // 0x067D
|
---|
618 | { 0xFB56, 3 }, // 0x067E D PEH
|
---|
619 | { 0xFB62, 3 }, // 0x067F D TEHEH
|
---|
620 |
|
---|
621 | { 0xFB5A, 3 }, // 0x0680 D BEHEH
|
---|
622 | { 0x0681, 0 }, // 0x0681
|
---|
623 | { 0x0682, 0 }, // 0x0682
|
---|
624 | { 0xFB76, 3 }, // 0x0683 D NYEH
|
---|
625 | { 0xFB72, 3 }, // 0x0684 D DYEH
|
---|
626 | { 0x0685, 0 }, // 0x0685
|
---|
627 | { 0xFB7A, 3 }, // 0x0686 D TCHEH
|
---|
628 | { 0xFB7E, 3 }, // 0x0687 D TCHEHEH
|
---|
629 | { 0xFB88, 1 }, // 0x0688 R DDAL
|
---|
630 | { 0x0689, 0 }, // 0x0689
|
---|
631 | { 0x068A, 0 }, // 0x068A
|
---|
632 | { 0x068B, 0 }, // 0x068B
|
---|
633 | { 0xFB84, 1 }, // 0x068C R DAHAL
|
---|
634 | { 0xFB82, 1 }, // 0x068D R DDAHAL
|
---|
635 | { 0xFB86, 1 }, // 0x068E R DUL
|
---|
636 | { 0x068F, 0 }, // 0x068F
|
---|
637 |
|
---|
638 | { 0x0690, 0 }, // 0x0690
|
---|
639 | { 0xFB8C, 1 }, // 0x0691 R RREH
|
---|
640 | { 0x0692, 0 }, // 0x0692
|
---|
641 | { 0x0693, 0 }, // 0x0693
|
---|
642 | { 0x0694, 0 }, // 0x0694
|
---|
643 | { 0x0695, 0 }, // 0x0695
|
---|
644 | { 0x0696, 0 }, // 0x0696
|
---|
645 | { 0x0697, 0 }, // 0x0697
|
---|
646 | { 0xFB8A, 1 }, // 0x0698 R JEH
|
---|
647 | { 0x0699, 0 }, // 0x0699
|
---|
648 | { 0x069A, 0 }, // 0x069A
|
---|
649 | { 0x069B, 0 }, // 0x069B
|
---|
650 | { 0x069C, 0 }, // 0x069C
|
---|
651 | { 0x069D, 0 }, // 0x069D
|
---|
652 | { 0x069E, 0 }, // 0x069E
|
---|
653 | { 0x069F, 0 }, // 0x069F
|
---|
654 |
|
---|
655 | { 0x06A0, 0 }, // 0x06A0
|
---|
656 | { 0x06A1, 0 }, // 0x06A1
|
---|
657 | { 0x06A2, 0 }, // 0x06A2
|
---|
658 | { 0x06A3, 0 }, // 0x06A3
|
---|
659 | { 0xFB6A, 3 }, // 0x06A4 D VEH
|
---|
660 | { 0x06A5, 0 }, // 0x06A5
|
---|
661 | { 0xFB6E, 3 }, // 0x06A6 D PEHEH
|
---|
662 | { 0x06A7, 0 }, // 0x06A7
|
---|
663 | { 0x06A8, 0 }, // 0x06A8
|
---|
664 | { 0xFB8E, 3 }, // 0x06A9 D KEHEH
|
---|
665 | { 0x06AA, 0 }, // 0x06AA
|
---|
666 | { 0x06AB, 0 }, // 0x06AB
|
---|
667 | { 0x06AC, 0 }, // 0x06AC
|
---|
668 | { 0xFBD3, 3 }, // 0x06AD D NG
|
---|
669 | { 0x06AE, 0 }, // 0x06AE
|
---|
670 | { 0xFB92, 3 }, // 0x06AF D GAF
|
---|
671 |
|
---|
672 | { 0x06B0, 0 }, // 0x06B0
|
---|
673 | { 0xFB9A, 3 }, // 0x06B1 D NGOEH
|
---|
674 | { 0x06B2, 0 }, // 0x06B2
|
---|
675 | { 0xFB96, 3 }, // 0x06B3 D GUEH
|
---|
676 | { 0x06B4, 0 }, // 0x06B4
|
---|
677 | { 0x06B5, 0 }, // 0x06B5
|
---|
678 | { 0x06B6, 0 }, // 0x06B6
|
---|
679 | { 0x06B7, 0 }, // 0x06B7
|
---|
680 | { 0x06B8, 0 }, // 0x06B8
|
---|
681 | { 0x06B9, 0 }, // 0x06B9
|
---|
682 | { 0x06BA, 0 }, // 0x06BA
|
---|
683 | { 0xFBA0, 3 }, // 0x06BB D RNOON
|
---|
684 | { 0x06BC, 0 }, // 0x06BC
|
---|
685 | { 0x06BD, 0 }, // 0x06BD
|
---|
686 | { 0xFBAA, 3 }, // 0x06BE D HEH DOACHASHMEE
|
---|
687 | { 0x06BF, 0 }, // 0x06BF
|
---|
688 |
|
---|
689 | { 0xFBA4, 1 }, // 0x06C0 R HEH WITH YEH ABOVE
|
---|
690 | { 0xFBA6, 3 }, // 0x06C1 D HEH GOAL
|
---|
691 | { 0x06C2, 0 }, // 0x06C2
|
---|
692 | { 0x06C3, 0 }, // 0x06C3
|
---|
693 | { 0x06C4, 0 }, // 0x06C4
|
---|
694 | { 0xFBE0, 1 }, // 0x06C5 R KIRGHIZ OE
|
---|
695 | { 0xFBD9, 1 }, // 0x06C6 R OE
|
---|
696 | { 0xFBD7, 1 }, // 0x06C7 R U
|
---|
697 | { 0xFBDB, 1 }, // 0x06C8 R YU
|
---|
698 | { 0xFBE2, 1 }, // 0x06C9 R KIRGHIZ YU
|
---|
699 | { 0x06CA, 0 }, // 0x06CA
|
---|
700 | { 0xFBDE, 1 }, // 0x06CB R VE
|
---|
701 | { 0xFBFC, 3 }, // 0x06CC D FARSI YEH
|
---|
702 | { 0x06CD, 0 }, // 0x06CD
|
---|
703 | { 0x06CE, 0 }, // 0x06CE
|
---|
704 | { 0x06CF, 0 }, // 0x06CF
|
---|
705 |
|
---|
706 | { 0xFBE4, 3 }, // 0x06D0 D E
|
---|
707 | { 0x06D1, 0 }, // 0x06D1
|
---|
708 | { 0xFBAE, 1 }, // 0x06D2 R YEH BARREE
|
---|
709 | { 0xFBB0, 1 }, // 0x06D3 R YEH BARREE WITH HAMZA ABOVE
|
---|
710 | { 0x06D4, 0 }, // 0x06D4
|
---|
711 | { 0x06D5, 0 }, // 0x06D5
|
---|
712 | { 0x06D6, 0 }, // 0x06D6
|
---|
713 | { 0x06D7, 0 }, // 0x06D7
|
---|
714 | { 0x06D8, 0 }, // 0x06D8
|
---|
715 | { 0x06D9, 0 }, // 0x06D9
|
---|
716 | { 0x06DA, 0 }, // 0x06DA
|
---|
717 | { 0x06DB, 0 }, // 0x06DB
|
---|
718 | { 0x06DC, 0 }, // 0x06DC
|
---|
719 | { 0x06DD, 0 }, // 0x06DD
|
---|
720 | { 0x06DE, 0 }, // 0x06DE
|
---|
721 | { 0x06DF, 0 }, // 0x06DF
|
---|
722 |
|
---|
723 | { 0x06E0, 0 }, // 0x06E0
|
---|
724 | { 0x06E1, 0 }, // 0x06E1
|
---|
725 | { 0x06E2, 0 }, // 0x06E2
|
---|
726 | { 0x06E3, 0 }, // 0x06E3
|
---|
727 | { 0x06E4, 0 }, // 0x06E4
|
---|
728 | { 0x06E5, 0 }, // 0x06E5
|
---|
729 | { 0x06E6, 0 }, // 0x06E6
|
---|
730 | { 0x06E7, 0 }, // 0x06E7
|
---|
731 | { 0x06E8, 0 }, // 0x06E8
|
---|
732 | { 0x06E9, 0 }, // 0x06E9
|
---|
733 | { 0x06EA, 0 }, // 0x06EA
|
---|
734 | { 0x06EB, 0 }, // 0x06EB
|
---|
735 | { 0x06EC, 0 }, // 0x06EC
|
---|
736 | { 0x06ED, 0 }, // 0x06ED
|
---|
737 | { 0x06EE, 0 }, // 0x06EE
|
---|
738 | { 0x06EF, 0 }, // 0x06EF
|
---|
739 |
|
---|
740 | { 0x06F0, 0 }, // 0x06F0
|
---|
741 | { 0x06F1, 0 }, // 0x06F1
|
---|
742 | { 0x06F2, 0 }, // 0x06F2
|
---|
743 | { 0x06F3, 0 }, // 0x06F3
|
---|
744 | { 0x06F4, 0 }, // 0x06F4
|
---|
745 | { 0x06F5, 0 }, // 0x06F5
|
---|
746 | { 0x06F6, 0 }, // 0x06F6
|
---|
747 | { 0x06F7, 0 }, // 0x06F7
|
---|
748 | { 0x06F8, 0 }, // 0x06F8
|
---|
749 | { 0x06F9, 0 }, // 0x06F9
|
---|
750 | { 0x06FA, 0 }, // 0x06FA
|
---|
751 | { 0x06FB, 0 }, // 0x06FB
|
---|
752 | { 0x06FC, 0 }, // 0x06FC
|
---|
753 | { 0x06FD, 0 }, // 0x06FD
|
---|
754 | { 0x06FE, 0 }, // 0x06FE
|
---|
755 | { 0x06FF, 0 } // 0x06FF
|
---|
756 | };
|
---|
757 |
|
---|
758 | // the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, this table does
|
---|
759 | static const ushort alefMaksura[4] = {0xFEEF, 0xFEF0, 0xFBE8, 0xFBE9};
|
---|
760 |
|
---|
761 | // this is a bit tricky. Alef always binds to the right, so the second parameter descibing the shape
|
---|
762 | // of the lam can be either initial of medial. So initial maps to the isolated form of the ligature,
|
---|
763 | // medial to the final form
|
---|
764 | static const ushort arabicUnicodeLamAlefMapping[6][4] = {
|
---|
765 | { 0xfffd, 0xfffd, 0xfef5, 0xfef6 }, // 0x622 R Alef with Madda above
|
---|
766 | { 0xfffd, 0xfffd, 0xfef7, 0xfef8 }, // 0x623 R Alef with Hamza above
|
---|
767 | { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x624 // Just to fill the table ;-)
|
---|
768 | { 0xfffd, 0xfffd, 0xfef9, 0xfefa }, // 0x625 R Alef with Hamza below
|
---|
769 | { 0xfffd, 0xfffd, 0xfffd, 0xfffd }, // 0x626 // Just to fill the table ;-)
|
---|
770 | { 0xfffd, 0xfffd, 0xfefb, 0xfefc } // 0x627 R Alef
|
---|
771 | };
|
---|
772 |
|
---|
773 | static inline int getShape( uchar cell, int shape )
|
---|
774 | {
|
---|
775 | #ifdef Q_OS_MAC
|
---|
776 | // the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here
|
---|
777 | uint ch = ( cell != 0x49 )
|
---|
778 | ? (shape ? arabicUnicodeMapping[cell][0] + shape : 0x600+cell)
|
---|
779 | : alefMaksura[shape] ;
|
---|
780 | #else
|
---|
781 | // the arabicUnicodeMapping does not work for U+0649 ALEF MAKSURA, handle this here
|
---|
782 | uint ch = ( cell != 0x49 ) ? arabicUnicodeMapping[cell][0] + shape
|
---|
783 | : alefMaksura[shape] ;
|
---|
784 | #endif
|
---|
785 | return ch;
|
---|
786 | }
|
---|
787 |
|
---|
788 |
|
---|
789 | static void shapedString(const QString& uc, int from, int len, QChar *shapeBuffer, int *shapedLength,
|
---|
790 | bool reverse, GlyphAttributes *attrs, unsigned short *logClusters )
|
---|
791 | {
|
---|
792 | if( len < 0 ) {
|
---|
793 | len = uc.length() - from;
|
---|
794 | } else if( len == 0 ) {
|
---|
795 | *shapedLength = 0;
|
---|
796 | return;
|
---|
797 | }
|
---|
798 |
|
---|
799 | const QChar *ch = uc.unicode() + from;
|
---|
800 | QChar *data = shapeBuffer;
|
---|
801 | int clusterStart = 0;
|
---|
802 |
|
---|
803 | for ( int i = 0; i < len; i++ ) {
|
---|
804 | uchar r = ch->row();
|
---|
805 | int gpos = data - shapeBuffer;
|
---|
806 |
|
---|
807 | if ( r != 0x06 ) {
|
---|
808 | if ( r == 0x20 ) {
|
---|
809 | uchar c = ch->cell();
|
---|
810 | if (c == 0x0c || c == 0x0d)
|
---|
811 | // remove ZWJ and ZWNJ
|
---|
812 | goto skip;
|
---|
813 | }
|
---|
814 | if ( reverse )
|
---|
815 | *data = mirroredChar( *ch );
|
---|
816 | else
|
---|
817 | *data = *ch;
|
---|
818 | } else {
|
---|
819 | uchar c = ch->cell();
|
---|
820 | int pos = i + from;
|
---|
821 | int shape = glyphVariantLogical( uc, pos );
|
---|
822 | // qDebug("mapping U+%x to shape %d glyph=0x%x", ch->unicode(), shape, getShape(c, shape));
|
---|
823 | // take care of lam-alef ligatures (lam right of alef)
|
---|
824 | ushort map;
|
---|
825 | switch ( c ) {
|
---|
826 | case 0x44: { // lam
|
---|
827 | const QChar *pch = nextChar( uc, pos );
|
---|
828 | if ( pch->row() == 0x06 ) {
|
---|
829 | switch ( pch->cell() ) {
|
---|
830 | case 0x22:
|
---|
831 | case 0x23:
|
---|
832 | case 0x25:
|
---|
833 | case 0x27:
|
---|
834 | // qDebug(" lam of lam-alef ligature");
|
---|
835 | map = arabicUnicodeLamAlefMapping[pch->cell() - 0x22][shape];
|
---|
836 | goto next;
|
---|
837 | default:
|
---|
838 | break;
|
---|
839 | }
|
---|
840 | }
|
---|
841 | break;
|
---|
842 | }
|
---|
843 | case 0x22: // alef with madda
|
---|
844 | case 0x23: // alef with hamza above
|
---|
845 | case 0x25: // alef with hamza below
|
---|
846 | case 0x27: // alef
|
---|
847 | if ( prevChar( uc, pos )->unicode() == 0x0644 ) {
|
---|
848 | // have a lam alef ligature
|
---|
849 | //qDebug(" alef of lam-alef ligature");
|
---|
850 | goto skip;
|
---|
851 | }
|
---|
852 | default:
|
---|
853 | break;
|
---|
854 | }
|
---|
855 | map = getShape( c, shape );
|
---|
856 | next:
|
---|
857 | *data = map;
|
---|
858 | }
|
---|
859 | // #### Qt 4.0: Fixme
|
---|
860 | attrs[gpos].zeroWidth = false;
|
---|
861 | if ( ::category( *ch ) == QChar::Mark_NonSpacing ) {
|
---|
862 | attrs[gpos].mark = TRUE;
|
---|
863 | // qDebug("glyph %d (char %d) is mark!", gpos, i );
|
---|
864 | } else {
|
---|
865 | attrs[gpos].mark = FALSE;
|
---|
866 | clusterStart = data - shapeBuffer;
|
---|
867 | }
|
---|
868 | attrs[gpos].clusterStart = !attrs[gpos].mark;
|
---|
869 | attrs[gpos].combiningClass = combiningClass( *ch );
|
---|
870 | data++;
|
---|
871 | skip:
|
---|
872 | ch++;
|
---|
873 | logClusters[i] = clusterStart;
|
---|
874 | }
|
---|
875 | *shapedLength = data - shapeBuffer;
|
---|
876 | }
|
---|
877 |
|
---|
878 | #if defined( Q_WS_X11) && !defined( QT_NO_XFTFREETYPE )
|
---|
879 |
|
---|
880 | static void arabicSyriacOpenTypeShape( int script, QOpenType *openType, const QString &string, int from,
|
---|
881 | int len, QTextEngine *engine, QScriptItem *si )
|
---|
882 | {
|
---|
883 | convertToCMap( string.unicode() + from, len, engine, si );
|
---|
884 | heuristicSetGlyphAttributes( string, from, len, engine, si );
|
---|
885 |
|
---|
886 | unsigned char gv[256];
|
---|
887 | unsigned char *glyphVariant = gv;
|
---|
888 | bool ap[256];
|
---|
889 | bool *apply = ap;
|
---|
890 |
|
---|
891 | if ( si->num_glyphs > 255 ) {
|
---|
892 | glyphVariant = (unsigned char *)malloc(si->num_glyphs*sizeof(unsigned char));
|
---|
893 | apply = (bool *)malloc(si->num_glyphs*sizeof(bool));
|
---|
894 | }
|
---|
895 |
|
---|
896 | for ( int i = 0; i < si->num_glyphs; i++ )
|
---|
897 | glyphVariant[i] = glyphVariantLogical( string, from + i );
|
---|
898 |
|
---|
899 | glyph_t *glyphs = engine->glyphs(si);
|
---|
900 | GlyphAttributes *attrs = engine->glyphAttributes(si);
|
---|
901 | unsigned short *logClusters = engine->logClusters(si);
|
---|
902 | unsigned short *uc = (unsigned short *)string.unicode() + from;
|
---|
903 |
|
---|
904 | // Hack to remove ZWJ and ZWNJ from rendered output.
|
---|
905 | int j = 0;
|
---|
906 | for ( int i = 0; i < si->num_glyphs; i++ ) {
|
---|
907 | if (uc[i] == 0x200c || uc[i] == 0x200d)
|
---|
908 | continue;
|
---|
909 | glyphs[j] = glyphs[i];
|
---|
910 | attrs[j] = attrs[i];
|
---|
911 | glyphVariant[j] = glyphVariant[i];
|
---|
912 | logClusters[i] = logClusters[j];
|
---|
913 | ++j;
|
---|
914 | }
|
---|
915 | si->num_glyphs = j;
|
---|
916 |
|
---|
917 | openType->init(glyphs, attrs, si->num_glyphs, logClusters, si->num_glyphs);
|
---|
918 |
|
---|
919 | // call features in the order defined by http://www.microsoft.com/typography/otfntdev/arabicot/shaping.htm
|
---|
920 | openType->applyGSUBFeature(FT_MAKE_TAG( 'c', 'c', 'm', 'p' ));
|
---|
921 |
|
---|
922 | if (script == QFont::Arabic) {
|
---|
923 | const struct {
|
---|
924 | int tag;
|
---|
925 | int shape;
|
---|
926 | } features[] = {
|
---|
927 | { FT_MAKE_TAG( 'i', 's', 'o', 'l' ), XIsolated },
|
---|
928 | { FT_MAKE_TAG( 'f', 'i', 'n', 'a' ), XFinal },
|
---|
929 | { FT_MAKE_TAG( 'm', 'e', 'd', 'i' ), XMedial },
|
---|
930 | { FT_MAKE_TAG( 'i', 'n', 'i', 't' ), XInitial }
|
---|
931 | };
|
---|
932 | for (int j = 0; j < 4; ++j) {
|
---|
933 | for ( int i = 0; i < si->num_glyphs; i++ )
|
---|
934 | apply[i] = (glyphVariant[i] == features[j].shape);
|
---|
935 | openType->applyGSUBFeature(features[j].tag, apply);
|
---|
936 | }
|
---|
937 | } else {
|
---|
938 | const struct {
|
---|
939 | int tag;
|
---|
940 | int shape;
|
---|
941 | } features[] = {
|
---|
942 | { FT_MAKE_TAG( 'i', 's', 'o', 'l' ), XIsolated },
|
---|
943 | { FT_MAKE_TAG( 'f', 'i', 'n', 'a' ), XFinal },
|
---|
944 | { FT_MAKE_TAG( 'f', 'i', 'n', '2' ), XFinal },
|
---|
945 | { FT_MAKE_TAG( 'f', 'i', 'n', '3' ), XFinal },
|
---|
946 | { FT_MAKE_TAG( 'm', 'e', 'd', 'i' ), XMedial },
|
---|
947 | { FT_MAKE_TAG( 'm', 'e', 'd', '2' ), XMedial },
|
---|
948 | { FT_MAKE_TAG( 'i', 'n', 'i', 't' ), XInitial }
|
---|
949 | };
|
---|
950 | for (int j = 0; j < 7; ++j) {
|
---|
951 | for ( int i = 0; i < si->num_glyphs; i++ )
|
---|
952 | apply[i] = (glyphVariant[i] == features[j].shape);
|
---|
953 | openType->applyGSUBFeature(features[j].tag, apply);
|
---|
954 | }
|
---|
955 | }
|
---|
956 | const int commonFeatures[] = {
|
---|
957 | // these features get applied to all glyphs and both scripts
|
---|
958 | FT_MAKE_TAG( 'r', 'l', 'i', 'g' ),
|
---|
959 | FT_MAKE_TAG( 'c', 'a', 'l', 't' ),
|
---|
960 | FT_MAKE_TAG( 'l', 'i', 'g', 'a' ),
|
---|
961 | FT_MAKE_TAG( 'd', 'l', 'i', 'g' )
|
---|
962 | };
|
---|
963 | for (int j = 0; j < 4; ++j)
|
---|
964 | openType->applyGSUBFeature(commonFeatures[j]);
|
---|
965 |
|
---|
966 | if (script == QFont::Arabic) {
|
---|
967 | const int features[] = {
|
---|
968 | FT_MAKE_TAG( 'c', 's', 'w', 'h' ),
|
---|
969 | // mset is used in old Win95 fonts that don't have a 'mark' positioning table.
|
---|
970 | FT_MAKE_TAG( 'm', 's', 'e', 't' )
|
---|
971 | };
|
---|
972 | for (int j = 0; j < 2; ++j)
|
---|
973 | openType->applyGSUBFeature(features[j]);
|
---|
974 | }
|
---|
975 |
|
---|
976 | openType->applyGPOSFeatures();
|
---|
977 | si->num_glyphs = 0;
|
---|
978 | openType->appendTo(engine, si);
|
---|
979 |
|
---|
980 | if (glyphVariant != gv) {
|
---|
981 | free(glyphVariant);
|
---|
982 | free(apply);
|
---|
983 | }
|
---|
984 | }
|
---|
985 |
|
---|
986 | #endif
|
---|
987 |
|
---|
988 | static void arabic_attributes( int /*script*/, const QString &text, int from, int len, QCharAttributes *attributes )
|
---|
989 | {
|
---|
990 | const QChar *uc = text.unicode() + from;
|
---|
991 | attributes += from;
|
---|
992 | for ( int i = 0; i < len; i++ ) {
|
---|
993 | QChar::Category cat = ::category( *uc );
|
---|
994 | attributes->whiteSpace = (cat == QChar::Separator_Space) && (uc->unicode() != 0xa0);
|
---|
995 | attributes->softBreak = FALSE;
|
---|
996 | attributes->charStop = (cat != QChar::Mark_NonSpacing);
|
---|
997 | attributes->wordStop = FALSE;
|
---|
998 | attributes->invalid = FALSE;
|
---|
999 | ++uc;
|
---|
1000 | ++attributes;
|
---|
1001 | }
|
---|
1002 | }
|
---|
1003 |
|
---|
1004 |
|
---|
1005 | // #### stil missing: identify invalid character combinations
|
---|
1006 | static void arabic_shape( int /*script*/, const QString &string, int from, int len,
|
---|
1007 | QTextEngine *engine, QScriptItem *si )
|
---|
1008 | {
|
---|
1009 | #if defined( Q_WS_X11) && !defined( QT_NO_XFTFREETYPE )
|
---|
1010 | QOpenType *openType = si->fontEngine->openType();
|
---|
1011 |
|
---|
1012 | if ( openType && openType->supportsScript( QFont::Arabic ) ) {
|
---|
1013 | arabicSyriacOpenTypeShape( QFont::Arabic, openType, string, from, len, engine, si );
|
---|
1014 | return;
|
---|
1015 | }
|
---|
1016 | #endif
|
---|
1017 |
|
---|
1018 | const QString &text = string;
|
---|
1019 |
|
---|
1020 | GlyphAttributes *glyphAttributes = engine->glyphAttributes( si );
|
---|
1021 | unsigned short *logClusters = engine->logClusters( si );
|
---|
1022 |
|
---|
1023 | QChar *shapedChars = (QChar *)::malloc( len * sizeof( QChar ) );
|
---|
1024 |
|
---|
1025 | int slen;
|
---|
1026 | shapedString( text, from, len, shapedChars, &slen, (si->analysis.bidiLevel%2),
|
---|
1027 | glyphAttributes, logClusters );
|
---|
1028 |
|
---|
1029 | convertToCMap( shapedChars, slen, engine, si );
|
---|
1030 | advance_t *advances = engine->advances(si);
|
---|
1031 | for (int i = 0; i < slen; ++i)
|
---|
1032 | if (glyphAttributes[i].mark)
|
---|
1033 | advances[i] = 0;
|
---|
1034 |
|
---|
1035 | ::free( shapedChars );
|
---|
1036 | q_heuristicPosition( engine, si );
|
---|
1037 | }
|
---|
1038 |
|
---|
1039 | #ifdef Q_WS_X11
|
---|
1040 | # include "qscriptengine_x11.cpp"
|
---|
1041 | #elif defined( Q_WS_WIN )
|
---|
1042 | # include "qscriptengine_win.cpp"
|
---|
1043 | #elif defined( Q_WS_PM )
|
---|
1044 | # include "qscriptengine_pm.cpp"
|
---|
1045 | #elif defined( Q_WS_MAC )
|
---|
1046 | # include "qscriptengine_mac.cpp"
|
---|
1047 | #elif defined( Q_WS_QWS )
|
---|
1048 | # include "qscriptengine_qws.cpp"
|
---|
1049 | #endif
|
---|