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