2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * This is part of HarfBuzz, an OpenType Layout engine library.
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
25 #include "harfbuzz-shaper.h"
26 #include "harfbuzz-shaper-private.h"
28 #include "harfbuzz-stream-private.h"
32 #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
33 #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
35 // -----------------------------------------------------------------------------------------------------
37 // The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
39 // -----------------------------------------------------------------------------------------------------
41 /* The Unicode algorithm does in our opinion allow line breaks at some
42 places they shouldn't be allowed. The following changes were thus
43 made in comparison to the Unicode reference:
63 // The following line break classes are not treated by the table:
64 // AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
67 // the first 4 values have to agree with the enum in QCharAttributes
68 ProhibitedBreak, // PB in table
69 DirectBreak, // DB in table
70 IndirectBreak, // IB in table
71 CombiningIndirectBreak, // CI in table
72 CombiningProhibitedBreak // CP in table
74 #define DB DirectBreak
75 #define IB IndirectBreak
76 #define CI CombiningIndirectBreak
77 #define CP CombiningProhibitedBreak
78 #define PB ProhibitedBreak
80 static const hb_uint8 breakTable[HB_LineBreak_JT+1][HB_LineBreak_JT+1] =
82 /* OP CL QU GL NS EX SY IS PR PO NU AL ID IN HY BA BB B2 ZW CM WJ H2 H3 JL JV JT */
83 /* OP */ { PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, PB, CP, PB, PB, PB, PB, PB, PB },
84 /* CL */ { DB, PB, IB, IB, PB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
85 /* QU */ { PB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
86 /* GL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
87 /* NS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
88 /* EX */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
89 /* SY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
90 /* IS */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
91 /* PR */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, DB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, IB },
92 /* PO */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
93 /* NU */ { IB, PB, IB, IB, IB, IB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
94 /* AL */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
95 /* ID */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
96 /* IN */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
97 /* HY */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
98 /* BA */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
99 /* BB */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
100 /* B2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, DB, DB, DB, DB, IB, IB, DB, PB, PB, CI, PB, DB, DB, DB, DB, DB },
101 /* ZW */ { DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, DB, PB, DB, DB, DB, DB, DB, DB, DB },
102 /* CM */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, DB, IB, IB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, DB },
103 /* WJ */ { IB, PB, IB, IB, IB, PB, PB, PB, IB, IB, IB, IB, IB, IB, IB, IB, IB, IB, PB, CI, PB, IB, IB, IB, IB, IB },
104 /* H2 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB },
105 /* H3 */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB },
106 /* JL */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, IB, IB, IB, IB, DB },
107 /* JV */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, IB, IB },
108 /* JT */ { DB, PB, IB, IB, IB, PB, PB, PB, DB, IB, DB, DB, DB, IB, IB, IB, DB, DB, PB, CI, PB, DB, DB, DB, DB, IB }
116 static const hb_uint8 graphemeTable[HB_Grapheme_LVT + 1][HB_Grapheme_LVT + 1] =
118 // Other, CR, LF, Control,Extend,L, V, T, LV, LVT
119 { true , true , true , true , true , true , true , true , true , true }, // Other,
120 { true , true , true , true , true , true , true , true , true , true }, // CR,
121 { true , false, true , true , true , true , true , true , true , true }, // LF,
122 { true , true , true , true , true , true , true , true , true , true }, // Control,
123 { false, true , true , true , false, false, false, false, false, false }, // Extend,
124 { true , true , true , true , true , false, true , true , true , true }, // L,
125 { true , true , true , true , true , false, false, true , false, true }, // V,
126 { true , true , true , true , true , true , false, false, false, false }, // T,
127 { true , true , true , true , true , false, true , true , true , true }, // LV,
128 { true , true , true , true , true , false, true , true , true , true }, // LVT
131 static void calcLineBreaks(const HB_UChar16 *uc, hb_uint32 len, HB_CharAttributes *charAttributes)
136 // ##### can this fail if the first char is a surrogate?
137 HB_LineBreakClass cls;
138 HB_GraphemeClass grapheme;
139 HB_GetGraphemeAndLineBreakClass(*uc, &grapheme, &cls);
140 // handle case where input starts with an LF
141 if (cls == HB_LineBreak_LF)
142 cls = HB_LineBreak_BK;
144 charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK);
145 charAttributes[0].charStop = true;
148 for (hb_uint32 i = 1; i < len; ++i) {
149 charAttributes[i].whiteSpace = false;
150 charAttributes[i].charStop = true;
152 HB_UChar32 code = uc[i];
153 HB_GraphemeClass ngrapheme;
154 HB_LineBreakClass ncls;
155 HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
156 charAttributes[i].charStop = graphemeTable[ngrapheme][grapheme];
158 if (ncls == HB_LineBreak_SG) {
159 if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) {
161 } else if (HB_IsLowSurrogate(uc[i]) && HB_IsHighSurrogate(uc[i-1])) {
162 code = HB_SurrogateToUcs4(uc[i-1], uc[i]);
163 HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
164 charAttributes[i].charStop = false;
166 ncls = HB_LineBreak_AL;
170 // set white space and char stop flag
171 if (ncls >= HB_LineBreak_SP)
172 charAttributes[i].whiteSpace = true;
174 HB_LineBreakType lineBreakType = HB_NoBreak;
175 if (cls >= HB_LineBreak_LF) {
176 lineBreakType = HB_ForcedBreak;
177 } else if(cls == HB_LineBreak_CR) {
178 lineBreakType = (ncls == HB_LineBreak_LF) ? HB_NoBreak : HB_ForcedBreak;
181 if (ncls == HB_LineBreak_SP)
182 goto next_no_cls_update;
183 if (ncls >= HB_LineBreak_CR)
186 // two complex chars (thai or lao), thai_attributes might override, but here we do a best guess
187 if (cls == HB_LineBreak_SA && ncls == HB_LineBreak_SA) {
188 lineBreakType = HB_Break;
194 if (tcls >= HB_LineBreak_SA)
195 tcls = HB_LineBreak_ID;
196 if (cls >= HB_LineBreak_SA)
197 cls = HB_LineBreak_ID;
199 int brk = breakTable[cls][tcls];
202 lineBreakType = HB_Break;
203 if (uc[i-1] == 0xad) // soft hyphen
204 lineBreakType = HB_SoftHyphen;
207 lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
209 case CombiningIndirectBreak:
210 lineBreakType = HB_NoBreak;
211 if (lcls == HB_LineBreak_SP){
213 charAttributes[i-2].lineBreakType = HB_Break;
215 goto next_no_cls_update;
218 case CombiningProhibitedBreak:
219 lineBreakType = HB_NoBreak;
220 if (lcls != HB_LineBreak_SP)
221 goto next_no_cls_update;
222 case ProhibitedBreak:
231 grapheme = ngrapheme;
232 charAttributes[i-1].lineBreakType = lineBreakType;
234 charAttributes[len-1].lineBreakType = HB_ForcedBreak;
237 // --------------------------------------------------------------------------------------------------------------------------------------------
241 // --------------------------------------------------------------------------------------------------------------------------------------------
243 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
245 int nmarks = glast - gfrom;
248 HB_Glyph *glyphs = item->glyphs;
249 HB_GlyphAttributes *attributes = item->attributes;
251 HB_GlyphMetrics baseMetrics;
252 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
254 if (item->item.script == HB_Script_Hebrew
255 && (-baseMetrics.y) > baseMetrics.height)
256 // we need to attach below the baseline, because of the hebrew iud.
257 baseMetrics.height = -baseMetrics.y;
259 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
260 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
262 HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
263 HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
264 if (size > HB_FIXED_CONSTANT(4))
265 offsetBase += HB_FIXED_CONSTANT(4);
268 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
269 // qDebug("offset = %f", offsetBase);
271 bool rightToLeft = item->item.bidiLevel % 2;
274 unsigned char lastCmb = 0;
275 HB_GlyphMetrics attachmentRect;
276 memset(&attachmentRect, 0, sizeof(attachmentRect));
278 for(i = 1; i <= nmarks; i++) {
279 HB_Glyph mark = glyphs[gfrom+i];
280 HB_GlyphMetrics markMetrics;
281 item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
284 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
286 HB_Fixed offset = offsetBase;
287 unsigned char cmb = attributes[gfrom+i].combiningClass;
289 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
290 // bits in the glyphAttributes structure.
292 // fixed position classes. We approximate by mapping to one of the others.
293 // currently I added only the ones for arabic, hebrew, lao and thai.
295 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
297 // add a bit more offset to arabic, a bit hacky
298 if (cmb >= 27 && cmb <= 36 && offset < 3)
301 if ((cmb >= 10 && cmb <= 18) ||
302 cmb == 20 || cmb == 22 ||
303 cmb == 29 || cmb == 32)
304 cmb = HB_Combining_Below;
306 else if (cmb == 23 || cmb == 27 || cmb == 28 ||
307 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
308 cmb = HB_Combining_Above;
310 else if (cmb == 9 || cmb == 103 || cmb == 118)
311 cmb = HB_Combining_BelowRight;
313 else if (cmb == 24 || cmb == 107 || cmb == 122)
314 cmb = HB_Combining_AboveRight;
316 cmb = HB_Combining_AboveLeft;
322 // combining marks of different class don't interact. Reset the rectangle.
323 if (cmb != lastCmb) {
324 //qDebug("resetting rect");
325 attachmentRect = baseMetrics;
329 case HB_Combining_DoubleBelow:
330 // ### wrong in rtl context!
331 case HB_Combining_BelowLeft:
333 case HB_Combining_BelowLeftAttached:
334 p.x += attachmentRect.x - markMetrics.x;
335 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
337 case HB_Combining_Below:
339 case HB_Combining_BelowAttached:
340 p.x += attachmentRect.x - markMetrics.x;
341 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
343 p.x += (attachmentRect.width - markMetrics.width) / 2;
345 case HB_Combining_BelowRight:
347 case HB_Combining_BelowRightAttached:
348 p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
349 p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
351 case HB_Combining_Left:
353 case HB_Combining_LeftAttached:
355 case HB_Combining_Right:
357 case HB_Combining_RightAttached:
359 case HB_Combining_DoubleAbove:
360 // ### wrong in RTL context!
361 case HB_Combining_AboveLeft:
363 case HB_Combining_AboveLeftAttached:
364 p.x += attachmentRect.x - markMetrics.x;
365 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
367 case HB_Combining_Above:
369 case HB_Combining_AboveAttached:
370 p.x += attachmentRect.x - markMetrics.x;
371 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
373 p.x += (attachmentRect.width - markMetrics.width) / 2;
375 case HB_Combining_AboveRight:
377 case HB_Combining_AboveRightAttached:
378 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
379 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
382 case HB_Combining_IotaSubscript:
386 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
387 markMetrics.x += p.x;
388 markMetrics.y += p.y;
390 HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
391 unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
392 unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
393 unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
394 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
395 attachmentRect = unitedAttachmentRect;
399 item->offsets[gfrom+i].x = p.x;
400 item->offsets[gfrom+i].y = p.y;
402 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
403 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
405 item->advances[gfrom+i] = 0;
409 void HB_HeuristicPosition(HB_ShaperItem *item)
411 HB_GetGlyphAdvances(item);
412 HB_GlyphAttributes *attributes = item->attributes;
415 int i = item->num_glyphs;
417 if (cEnd == -1 && attributes[i].mark) {
419 } else if (cEnd != -1 && !attributes[i].mark) {
420 positionCluster(item, i, cEnd);
426 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
427 // and no reordering.
428 // also computes logClusters heuristically
429 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
431 const HB_UChar16 *uc = item->string + item->item.pos;
432 hb_uint32 length = item->item.length;
434 // ### zeroWidth and justification are missing here!!!!!
436 assert(length <= item->num_glyphs);
438 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
439 HB_GlyphAttributes *attributes = item->attributes;
440 unsigned short *logClusters = item->log_clusters;
442 hb_uint32 glyph_pos = 0;
444 for (i = 0; i < length; i++) {
445 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
446 && HB_IsLowSurrogate(uc[i + 1])) {
447 logClusters[i] = glyph_pos;
448 logClusters[++i] = glyph_pos;
450 logClusters[i] = glyph_pos;
455 // first char in a run is never (treated as) a mark
457 const bool symbolFont = item->face->isSymbolFont;
458 attributes[0].mark = false;
459 attributes[0].clusterStart = true;
460 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
463 HB_CharCategory lastCat;
465 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
466 for (i = 1; i < length; ++i) {
467 if (logClusters[i] == pos)
471 while (pos < logClusters[i]) {
472 attributes[pos] = attributes[pos-1];
475 // hide soft-hyphens by default
476 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
477 attributes[pos].dontPrint = true;
480 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
481 if (cat != HB_Mark_NonSpacing) {
482 attributes[pos].mark = false;
483 attributes[pos].clusterStart = true;
484 attributes[pos].combiningClass = 0;
485 cStart = logClusters[i];
488 // Fix 0 combining classes
489 if ((uc[pos] & 0xff00) == 0x0e00) {
491 if (uc[pos] == 0xe31 ||
500 cmb = HB_Combining_AboveRight;
501 } else if (uc[pos] == 0xeb1 ||
509 cmb = HB_Combining_Above;
510 } else if (uc[pos] == 0xebc) {
511 cmb = HB_Combining_Below;
516 attributes[pos].mark = true;
517 attributes[pos].clusterStart = false;
518 attributes[pos].combiningClass = cmb;
519 logClusters[i] = cStart;
521 // one gets an inter character justification point if the current char is not a non spacing mark.
522 // as then the current char belongs to the last one and one gets a space justification point
523 // after the space char.
524 if (lastCat == HB_Separator_Space)
525 attributes[pos-1].justification = HB_Space;
526 else if (cat != HB_Mark_NonSpacing)
527 attributes[pos-1].justification = HB_Character;
529 attributes[pos-1].justification = HB_NoJustification;
533 pos = logClusters[length-1];
534 if (lastCat == HB_Separator_Space)
535 attributes[pos].justification = HB_Space;
537 attributes[pos].justification = HB_Character;
541 static const HB_OpenTypeFeature basic_features[] = {
542 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
543 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
544 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
549 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
551 if (shaper_item->glyphIndicesPresent) {
552 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
553 shaper_item->glyphIndicesPresent = false;
556 return shaper_item->font->klass
557 ->convertStringToGlyphIndices(shaper_item->font,
558 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
559 shaper_item->glyphs, &shaper_item->num_glyphs,
560 shaper_item->item.bidiLevel % 2);
563 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
566 const int availableGlyphs = shaper_item->num_glyphs;
569 if (!HB_ConvertStringToGlyphIndices(shaper_item))
572 HB_HeuristicSetGlyphAttributes(shaper_item);
575 if (HB_SelectScript(shaper_item, basic_features)) {
576 HB_OpenTypeShape(shaper_item, /*properties*/0);
577 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
581 HB_HeuristicPosition(shaper_item);
585 const HB_ScriptEngine HB_ScriptEngines[] = {
595 { HB_HebrewShape, 0 },
597 { HB_ArabicShape, 0},
599 { HB_ArabicShape, 0},
601 { HB_BasicShape, 0 },
603 { HB_IndicShape, HB_IndicAttributes },
605 { HB_IndicShape, HB_IndicAttributes },
607 { HB_IndicShape, HB_IndicAttributes },
609 { HB_IndicShape, HB_IndicAttributes },
611 { HB_IndicShape, HB_IndicAttributes },
613 { HB_IndicShape, HB_IndicAttributes },
615 { HB_IndicShape, HB_IndicAttributes },
617 { HB_IndicShape, HB_IndicAttributes },
619 { HB_IndicShape, HB_IndicAttributes },
621 { HB_IndicShape, HB_IndicAttributes },
623 { HB_BasicShape, HB_ThaiAttributes },
625 { HB_BasicShape, 0 },
627 { HB_TibetanShape, HB_TibetanAttributes },
629 { HB_MyanmarShape, HB_MyanmarAttributes },
631 { HB_BasicShape, 0 },
633 { HB_HangulShape, 0 },
635 { HB_BasicShape, 0 },
637 { HB_BasicShape, 0 },
639 { HB_KhmerShape, HB_KhmerAttributes },
644 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
645 const HB_ScriptItem *items, hb_uint32 numItems,
646 HB_CharAttributes *attributes)
648 calcLineBreaks(string, stringLength, attributes);
650 for (hb_uint32 i = 0; i < numItems; ++i) {
651 HB_Script script = items[i].script;
652 if (script == HB_Script_Inherited)
653 script = HB_Script_Common;
654 HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
655 if (!attributeFunction)
657 attributeFunction(script, string, items[i].pos, items[i].length, attributes);
662 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
664 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
665 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
666 { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
667 { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
668 { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
669 { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
670 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
671 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
672 { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
673 { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
676 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
677 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
678 HB_CharAttributes *attributes)
680 if (stringLength == 0)
682 unsigned int brk = HB_GetWordClass(string[0]);
683 attributes[0].wordBoundary = true;
684 for (hb_uint32 i = 1; i < stringLength; ++i) {
685 if (!attributes[i].charStop) {
686 attributes[i].wordBoundary = false;
689 hb_uint32 nbrk = HB_GetWordClass(string[i]);
690 if (nbrk == HB_Word_Format) {
691 attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
694 BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
695 if (rule == Middle) {
697 hb_uint32 lookahead = i + 1;
698 while (lookahead < stringLength) {
699 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
700 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
704 if (testbrk == brk) {
706 while (i < lookahead)
707 attributes[i++].wordBoundary = false;
713 attributes[i].wordBoundary = (rule == Break);
719 enum SentenceBreakStates {
734 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
735 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
736 { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
737 { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
739 { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
740 { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
741 { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
742 { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
744 { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
745 { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
746 { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
747 { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
750 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
751 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
752 HB_CharAttributes *attributes)
754 if (stringLength == 0)
756 hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
757 attributes[0].sentenceBoundary = true;
758 for (hb_uint32 i = 1; i < stringLength; ++i) {
759 if (!attributes[i].charStop) {
760 attributes[i].sentenceBoundary = false;
763 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
764 if (brk == SB_Look) {
766 hb_uint32 lookahead = i + 1;
767 while (lookahead < stringLength) {
768 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
769 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
771 } else if (sbrk == HB_Sentence_Lower) {
777 if (brk == SB_Initial) {
778 while (i < lookahead)
779 attributes[i++].sentenceBoundary = false;
782 if (brk == SB_Break) {
783 attributes[i].sentenceBoundary = true;
784 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
786 attributes[i].sentenceBoundary = false;
792 static inline char *tag_to_string(HB_UInt tag)
794 static char string[5];
795 string[0] = (tag >> 24)&0xff;
796 string[1] = (tag >> 16)&0xff;
797 string[2] = (tag >> 8)&0xff;
798 string[3] = tag&0xff;
804 static void dump_string(HB_Buffer buffer)
806 for (uint i = 0; i < buffer->in_length; ++i) {
807 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
812 #define DEBUG if (1) ; else printf
815 #define DefaultLangSys 0xffff
816 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
827 static const OTScripts ot_scripts [] = {
829 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
831 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
833 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
835 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
837 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
839 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
841 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
843 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
845 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
847 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
849 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
851 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
853 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
855 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
857 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
859 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
861 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
863 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
865 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
867 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
869 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
871 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
873 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
875 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
877 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
879 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
881 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
883 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
885 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
887 static HB_Bool checkScript(HB_Face face, int script)
889 assert(script < HB_ScriptCount);
891 if (!face->gsub && !face->gpos)
894 unsigned int tag = ot_scripts[script].tag;
895 int requirements = ot_scripts[script].flags;
897 if (requirements & RequiresGsub) {
901 HB_UShort script_index;
902 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
904 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
905 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
911 if (requirements & RequiresGpos) {
915 HB_UShort script_index;
916 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
918 DEBUG("could not select script in gpos table: %d", error);
919 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
928 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
932 HB_Stream stream = 0;
937 error = tableFunc(font, tag, 0, &length);
940 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
943 stream->base = (HB_Byte*)malloc(length);
948 error = tableFunc(font, tag, stream->base, &length);
950 _hb_close_stream(stream);
953 stream->size = length;
955 stream->cursor = NULL;
959 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
961 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
965 face->isSymbolFont = false;
969 face->current_script = HB_ScriptCount;
970 face->current_flags = HB_ShaperFlag_Default;
971 face->has_opentype_kerning = false;
972 face->tmpAttributes = 0;
973 face->tmpLogClusters = 0;
974 face->glyphs_substituted = false;
979 HB_Stream gdefStream;
981 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
982 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
983 //DEBUG("error loading gdef table: %d", error);
987 //DEBUG() << "trying to load gsub table";
988 stream = getTableStream(font, tableFunc, TTAG_GSUB);
989 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
991 if (error != HB_Err_Not_Covered) {
992 //DEBUG("error loading gsub table: %d", error);
994 //DEBUG("face doesn't have a gsub table");
997 _hb_close_stream(stream);
999 stream = getTableStream(font, tableFunc, TTAG_GPOS);
1000 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1002 DEBUG("error loading gpos table: %d", error);
1004 _hb_close_stream(stream);
1006 _hb_close_stream(gdefStream);
1008 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1009 face->supported_scripts[i] = checkScript(face, i);
1011 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1019 void HB_FreeFace(HB_Face face)
1024 HB_Done_GPOS_Table(face->gpos);
1026 HB_Done_GSUB_Table(face->gsub);
1028 HB_Done_GDEF_Table(face->gdef);
1030 hb_buffer_free(face->buffer);
1031 if (face->tmpAttributes)
1032 free(face->tmpAttributes);
1033 if (face->tmpLogClusters)
1034 free(face->tmpLogClusters);
1038 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1040 HB_Script script = shaper_item->item.script;
1042 if (!shaper_item->face->supported_scripts[script])
1045 HB_Face face = shaper_item->face;
1046 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1049 face->current_script = script;
1050 face->current_flags = shaper_item->shaperFlags;
1052 assert(script < HB_ScriptCount);
1053 // find script in our list of supported scripts.
1054 unsigned int tag = ot_scripts[script].tag;
1056 if (face->gsub && features) {
1059 HB_FeatureList featurelist = face->gsub->FeatureList;
1060 int numfeatures = featurelist.FeatureCount;
1061 DEBUG("gsub table has %d features", numfeatures);
1062 for (int i = 0; i < numfeatures; i++) {
1063 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1064 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1068 HB_GSUB_Clear_Features(face->gsub);
1069 HB_UShort script_index;
1070 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1072 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1073 while (features->tag) {
1074 HB_UShort feature_index;
1075 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1077 DEBUG(" adding feature %s", tag_to_string(features->tag));
1078 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1086 face->has_opentype_kerning = false;
1089 HB_GPOS_Clear_Features(face->gpos);
1090 HB_UShort script_index;
1091 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1095 HB_FeatureList featurelist = face->gpos->FeatureList;
1096 int numfeatures = featurelist.FeatureCount;
1097 DEBUG("gpos table has %d features", numfeatures);
1098 for(int i = 0; i < numfeatures; i++) {
1099 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1100 HB_UShort feature_index;
1101 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1102 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1106 HB_UInt *feature_tag_list_buffer;
1107 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1109 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1110 while (*feature_tag_list) {
1111 HB_UShort feature_index;
1112 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1113 if (face->current_flags & HB_ShaperFlag_NoKerning) {
1117 face->has_opentype_kerning = true;
1119 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1121 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1124 FREE(feature_tag_list_buffer);
1132 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1134 HB_GlyphAttributes *tmpAttributes;
1135 unsigned int *tmpLogClusters;
1137 HB_Face face = item->face;
1139 face->length = item->num_glyphs;
1141 hb_buffer_clear(face->buffer);
1143 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1146 face->tmpAttributes = tmpAttributes;
1148 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1149 if (!tmpLogClusters)
1151 face->tmpLogClusters = tmpLogClusters;
1153 for (int i = 0; i < face->length; ++i) {
1154 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1155 face->tmpAttributes[i] = item->attributes[i];
1156 face->tmpLogClusters[i] = item->log_clusters[i];
1160 DEBUG("-----------------------------------------");
1161 // DEBUG("log clusters before shaping:");
1162 // for (int j = 0; j < length; j++)
1163 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1164 DEBUG("original glyphs: %p", item->glyphs);
1165 for (int i = 0; i < length; ++i)
1166 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1167 // dump_string(hb_buffer);
1170 face->glyphs_substituted = false;
1172 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1173 if (error && error != HB_Err_Not_Covered)
1175 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1179 // DEBUG("log clusters before shaping:");
1180 // for (int j = 0; j < length; j++)
1181 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1182 DEBUG("shaped glyphs:");
1183 for (int i = 0; i < length; ++i)
1184 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1185 DEBUG("-----------------------------------------");
1186 // dump_string(hb_buffer);
1192 /* See comments near the definition of HB_ShaperFlag_ForceMarksToZeroWidth for a description
1193 of why this function exists. */
1194 void HB_FixupZeroWidth(HB_ShaperItem *item)
1198 if (!item->face->gdef)
1201 for (unsigned int i = 0; i < item->num_glyphs; ++i) {
1202 /* If the glyph is a mark, force its advance to zero. */
1203 if (HB_GDEF_Get_Glyph_Property (item->face->gdef, item->glyphs[i], &property) == HB_Err_Ok &&
1204 property == HB_GDEF_MARK) {
1205 item->advances[i] = 0;
1210 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1212 HB_Face face = item->face;
1214 bool glyphs_positioned = false;
1216 if (face->buffer->positions)
1217 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1218 // #### check that passing "false,false" is correct
1219 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1222 if (!face->glyphs_substituted && !glyphs_positioned) {
1223 HB_GetGlyphAdvances(item);
1224 if (item->face->current_flags & HB_ShaperFlag_ForceMarksToZeroWidth)
1225 HB_FixupZeroWidth(item);
1226 return true; // nothing to do for us
1229 // make sure we have enough space to write everything back
1230 if (availableGlyphs < (int)face->buffer->in_length) {
1231 item->num_glyphs = face->buffer->in_length;
1235 HB_Glyph *glyphs = item->glyphs;
1236 HB_GlyphAttributes *attributes = item->attributes;
1238 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1239 glyphs[i] = face->buffer->in_string[i].gindex;
1240 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
1241 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
1242 attributes[i].clusterStart = false;
1244 item->num_glyphs = face->buffer->in_length;
1246 if (doLogClusters && face->glyphs_substituted) {
1247 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1248 unsigned short *logClusters = item->log_clusters;
1249 int clusterStart = 0;
1251 // #### the reconstruction of the logclusters currently does not work if the original string
1252 // contains surrogate pairs
1253 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1254 int ci = face->buffer->in_string[i].cluster;
1255 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1256 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1257 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1258 for (int j = oldCi; j < ci; j++)
1259 logClusters[j] = clusterStart;
1264 for (int j = oldCi; j < face->length; j++)
1265 logClusters[j] = clusterStart;
1268 // calulate the advances for the shaped glyphs
1269 // DEBUG("unpositioned: ");
1271 // positioning code:
1272 if (glyphs_positioned) {
1273 HB_GetGlyphAdvances(item);
1274 HB_Position positions = face->buffer->positions;
1275 HB_Fixed *advances = item->advances;
1277 // DEBUG("positioned glyphs:");
1278 for (unsigned int i = 0; i < face->buffer->in_length; i++) {
1279 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1280 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1281 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1282 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1283 // positions[i].back, positions[i].new_advance);
1285 HB_Fixed adjustment = (item->item.bidiLevel % 2) ? -positions[i].x_advance : positions[i].x_advance;
1287 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1288 adjustment = HB_FIXED_ROUND(adjustment);
1290 if (positions[i].new_advance) {
1291 advances[i] = adjustment;
1293 advances[i] += adjustment;
1297 HB_FixedPoint *offsets = item->offsets;
1298 offsets[i].x = positions[i].x_pos;
1299 offsets[i].y = positions[i].y_pos;
1300 while (positions[i - back].back) {
1301 back += positions[i - back].back;
1302 offsets[i].x += positions[i - back].x_pos;
1303 offsets[i].y += positions[i - back].y_pos;
1305 offsets[i].y = -offsets[i].y;
1307 if (item->item.bidiLevel % 2) {
1308 // ### may need to go back multiple glyphs like in ltr
1309 back = positions[i].back;
1311 offsets[i].x -= advances[i-back];
1314 while (positions[i - back].back) {
1315 back += positions[i - back].back;
1316 offsets[i].x -= advances[i-back];
1319 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1320 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1322 item->kerning_applied = face->has_opentype_kerning;
1324 HB_HeuristicPosition(item);
1328 if (doLogClusters) {
1329 DEBUG("log clusters after shaping:");
1330 for (int j = 0; j < length; j++)
1331 DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1333 DEBUG("final glyphs:");
1334 for (int i = 0; i < (int)hb_buffer->in_length; ++i)
1335 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
1336 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
1337 glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
1338 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1339 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1340 DEBUG("-----------------------------------------");
1345 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1347 HB_Bool result = false;
1348 if (shaper_item->num_glyphs < shaper_item->item.length) {
1349 shaper_item->num_glyphs = shaper_item->item.length;
1352 assert(shaper_item->item.script < HB_ScriptCount);
1353 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1354 shaper_item->glyphIndicesPresent = false;