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)
138 // ##### can this fail if the first char is a surrogate?
139 HB_LineBreakClass cls;
140 HB_GraphemeClass grapheme;
141 HB_GetGraphemeAndLineBreakClass(*uc, &grapheme, &cls);
142 // handle case where input starts with an LF
143 if (cls == HB_LineBreak_LF)
144 cls = HB_LineBreak_BK;
146 charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK);
147 charAttributes[0].charStop = true;
150 for (i = 1; i < len; ++i) {
151 HB_UChar32 code = uc[i];
152 HB_GraphemeClass ngrapheme;
153 HB_LineBreakClass ncls;
155 charAttributes[i].whiteSpace = false;
156 charAttributes[i].charStop = true;
158 HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
159 charAttributes[i].charStop = graphemeTable[ngrapheme][grapheme];
161 if (ncls == HB_LineBreak_SG) {
162 if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) {
164 } else if (HB_IsLowSurrogate(uc[i]) && HB_IsHighSurrogate(uc[i-1])) {
165 code = HB_SurrogateToUcs4(uc[i-1], uc[i]);
166 HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
167 charAttributes[i].charStop = false;
169 ncls = HB_LineBreak_AL;
173 // set white space and char stop flag
174 if (ncls >= HB_LineBreak_SP)
175 charAttributes[i].whiteSpace = true;
177 HB_LineBreakType lineBreakType = HB_NoBreak;
178 if (cls >= HB_LineBreak_LF) {
179 lineBreakType = HB_ForcedBreak;
180 } else if(cls == HB_LineBreak_CR) {
181 lineBreakType = (ncls == HB_LineBreak_LF) ? HB_NoBreak : HB_ForcedBreak;
184 if (ncls == HB_LineBreak_SP)
185 goto next_no_cls_update;
186 if (ncls >= HB_LineBreak_CR)
189 // two complex chars (thai or lao), thai_attributes might override, but here we do a best guess
190 if (cls == HB_LineBreak_SA && ncls == HB_LineBreak_SA) {
191 lineBreakType = HB_Break;
197 if (tcls >= HB_LineBreak_SA)
198 tcls = HB_LineBreak_ID;
199 if (cls >= HB_LineBreak_SA)
200 cls = HB_LineBreak_ID;
202 int brk = breakTable[cls][tcls];
205 lineBreakType = HB_Break;
206 if (uc[i-1] == 0xad) // soft hyphen
207 lineBreakType = HB_SoftHyphen;
210 lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
212 case CombiningIndirectBreak:
213 lineBreakType = HB_NoBreak;
214 if (lcls == HB_LineBreak_SP){
216 charAttributes[i-2].lineBreakType = HB_Break;
218 goto next_no_cls_update;
221 case CombiningProhibitedBreak:
222 lineBreakType = HB_NoBreak;
223 if (lcls != HB_LineBreak_SP)
224 goto next_no_cls_update;
225 case ProhibitedBreak:
234 grapheme = ngrapheme;
235 charAttributes[i-1].lineBreakType = lineBreakType;
237 charAttributes[len-1].lineBreakType = HB_ForcedBreak;
240 // --------------------------------------------------------------------------------------------------------------------------------------------
244 // --------------------------------------------------------------------------------------------------------------------------------------------
246 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
248 int nmarks = glast - gfrom;
249 HB_Bool rightToLeft = item->item.bidiLevel % 2;
251 unsigned char lastCmb = 0;
252 HB_GlyphMetrics attachmentRect;
256 HB_Glyph *glyphs = item->glyphs;
257 HB_GlyphAttributes *attributes = item->attributes;
259 HB_GlyphMetrics baseMetrics;
260 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
262 if (item->item.script == HB_Script_Hebrew
263 && (-baseMetrics.y) > baseMetrics.height)
264 // we need to attach below the baseline, because of the hebrew iud.
265 baseMetrics.height = -baseMetrics.y;
267 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
268 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
270 HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
271 HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
272 if (size > HB_FIXED_CONSTANT(4))
273 offsetBase += HB_FIXED_CONSTANT(4);
276 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
277 // qDebug("offset = %f", offsetBase);
279 memset(&attachmentRect, 0, sizeof(attachmentRect));
281 for(i = 1; i <= nmarks; i++) {
282 HB_Glyph mark = glyphs[gfrom+i];
283 HB_GlyphMetrics markMetrics;
284 HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
286 item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
289 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
291 HB_Fixed offset = offsetBase;
292 unsigned char cmb = attributes[gfrom+i].combiningClass;
294 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
295 // bits in the glyphAttributes structure.
297 // fixed position classes. We approximate by mapping to one of the others.
298 // currently I added only the ones for arabic, hebrew, lao and thai.
300 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
302 // add a bit more offset to arabic, a bit hacky
303 if (cmb >= 27 && cmb <= 36 && offset < 3)
306 if ((cmb >= 10 && cmb <= 18) ||
307 cmb == 20 || cmb == 22 ||
308 cmb == 29 || cmb == 32)
309 cmb = HB_Combining_Below;
311 else if (cmb == 23 || cmb == 27 || cmb == 28 ||
312 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
313 cmb = HB_Combining_Above;
315 else if (cmb == 9 || cmb == 103 || cmb == 118)
316 cmb = HB_Combining_BelowRight;
318 else if (cmb == 24 || cmb == 107 || cmb == 122)
319 cmb = HB_Combining_AboveRight;
321 cmb = HB_Combining_AboveLeft;
327 // combining marks of different class don't interact. Reset the rectangle.
328 if (cmb != lastCmb) {
329 //qDebug("resetting rect");
330 attachmentRect = baseMetrics;
334 case HB_Combining_DoubleBelow:
335 // ### wrong in rtl context!
336 case HB_Combining_BelowLeft:
338 case HB_Combining_BelowLeftAttached:
339 p.x += attachmentRect.x - markMetrics.x;
340 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
342 case HB_Combining_Below:
344 case HB_Combining_BelowAttached:
345 p.x += attachmentRect.x - markMetrics.x;
346 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
348 p.x += (attachmentRect.width - markMetrics.width) / 2;
350 case HB_Combining_BelowRight:
352 case HB_Combining_BelowRightAttached:
353 p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
354 p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
356 case HB_Combining_Left:
358 case HB_Combining_LeftAttached:
360 case HB_Combining_Right:
362 case HB_Combining_RightAttached:
364 case HB_Combining_DoubleAbove:
365 // ### wrong in RTL context!
366 case HB_Combining_AboveLeft:
368 case HB_Combining_AboveLeftAttached:
369 p.x += attachmentRect.x - markMetrics.x;
370 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
372 case HB_Combining_Above:
374 case HB_Combining_AboveAttached:
375 p.x += attachmentRect.x - markMetrics.x;
376 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
378 p.x += (attachmentRect.width - markMetrics.width) / 2;
380 case HB_Combining_AboveRight:
382 case HB_Combining_AboveRightAttached:
383 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
384 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
387 case HB_Combining_IotaSubscript:
391 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
392 markMetrics.x += p.x;
393 markMetrics.y += p.y;
395 unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
396 unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
397 unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
398 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
399 attachmentRect = unitedAttachmentRect;
403 item->offsets[gfrom+i].x = p.x;
404 item->offsets[gfrom+i].y = p.y;
406 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
407 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
409 item->advances[gfrom+i] = 0;
413 void HB_HeuristicPosition(HB_ShaperItem *item)
415 HB_GetGlyphAdvances(item);
416 HB_GlyphAttributes *attributes = item->attributes;
419 int i = item->num_glyphs;
421 if (cEnd == -1 && attributes[i].mark) {
423 } else if (cEnd != -1 && !attributes[i].mark) {
424 positionCluster(item, i, cEnd);
430 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
431 // and no reordering.
432 // also computes logClusters heuristically
433 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
435 const HB_UChar16 *uc = item->string + item->item.pos;
436 hb_uint32 length = item->item.length;
438 // ### zeroWidth and justification are missing here!!!!!
440 assert(item->num_glyphs <= length);
442 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
443 HB_GlyphAttributes *attributes = item->attributes;
444 unsigned short *logClusters = item->log_clusters;
446 hb_uint32 glyph_pos = 0;
449 const bool symbolFont = item->face->isSymbolFont;
451 for (i = 0; i < length; i++) {
452 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
453 && HB_IsLowSurrogate(uc[i + 1])) {
454 logClusters[i] = glyph_pos;
455 logClusters[++i] = glyph_pos;
457 logClusters[i] = glyph_pos;
461 assert(glyph_pos == item->num_glyphs);
463 // first char in a run is never (treated as) a mark
464 attributes[0].mark = false;
465 attributes[0].clusterStart = true;
466 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
469 HB_CharCategory lastCat;
471 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
472 for (i = 1; i < length; ++i) {
473 if (logClusters[i] == pos)
477 while (pos < logClusters[i]) {
478 attributes[pos] = attributes[pos-1];
481 // hide soft-hyphens by default
482 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
483 attributes[pos].dontPrint = true;
486 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
487 if (cat != HB_Mark_NonSpacing) {
488 attributes[pos].mark = false;
489 attributes[pos].clusterStart = true;
490 attributes[pos].combiningClass = 0;
491 cStart = logClusters[i];
494 // Fix 0 combining classes
495 if ((uc[pos] & 0xff00) == 0x0e00) {
497 if (uc[pos] == 0xe31 ||
506 cmb = HB_Combining_AboveRight;
507 } else if (uc[pos] == 0xeb1 ||
515 cmb = HB_Combining_Above;
516 } else if (uc[pos] == 0xebc) {
517 cmb = HB_Combining_Below;
522 attributes[pos].mark = true;
523 attributes[pos].clusterStart = false;
524 attributes[pos].combiningClass = cmb;
525 logClusters[i] = cStart;
527 // one gets an inter character justification point if the current char is not a non spacing mark.
528 // as then the current char belongs to the last one and one gets a space justification point
529 // after the space char.
530 if (lastCat == HB_Separator_Space)
531 attributes[pos-1].justification = HB_Space;
532 else if (cat != HB_Mark_NonSpacing)
533 attributes[pos-1].justification = HB_Character;
535 attributes[pos-1].justification = HB_NoJustification;
539 pos = logClusters[length-1];
540 if (lastCat == HB_Separator_Space)
541 attributes[pos].justification = HB_Space;
543 attributes[pos].justification = HB_Character;
547 static const HB_OpenTypeFeature basic_features[] = {
548 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
549 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
550 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
555 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
557 if (shaper_item->glyphIndicesPresent) {
558 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
559 shaper_item->glyphIndicesPresent = false;
562 return shaper_item->font->klass
563 ->convertStringToGlyphIndices(shaper_item->font,
564 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
565 shaper_item->glyphs, &shaper_item->num_glyphs,
566 shaper_item->item.bidiLevel % 2);
569 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
572 const int availableGlyphs = shaper_item->num_glyphs;
575 if (!HB_ConvertStringToGlyphIndices(shaper_item))
578 HB_HeuristicSetGlyphAttributes(shaper_item);
581 if (HB_SelectScript(shaper_item, basic_features)) {
582 HB_OpenTypeShape(shaper_item, /*properties*/0);
583 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
587 HB_HeuristicPosition(shaper_item);
591 const HB_ScriptEngine HB_ScriptEngines[] = {
601 { HB_HebrewShape, 0 },
603 { HB_ArabicShape, 0},
605 { HB_ArabicShape, 0},
607 { HB_BasicShape, 0 },
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_IndicShape, HB_IndicAttributes },
625 { HB_IndicShape, HB_IndicAttributes },
627 { HB_IndicShape, HB_IndicAttributes },
629 { HB_BasicShape, HB_ThaiAttributes },
631 { HB_BasicShape, 0 },
633 { HB_TibetanShape, HB_TibetanAttributes },
635 { HB_MyanmarShape, HB_MyanmarAttributes },
637 { HB_BasicShape, 0 },
639 { HB_HangulShape, 0 },
641 { HB_BasicShape, 0 },
643 { HB_BasicShape, 0 },
645 { HB_KhmerShape, HB_KhmerAttributes },
650 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
651 const HB_ScriptItem *items, hb_uint32 numItems,
652 HB_CharAttributes *attributes)
655 calcLineBreaks(string, stringLength, attributes);
657 for (i = 0; i < numItems; ++i) {
658 HB_Script script = items[i].script;
659 HB_AttributeFunction attributeFunction = NULL ;
660 if (script == HB_Script_Inherited)
661 script = HB_Script_Common;
662 attributeFunction = HB_ScriptEngines[script].charAttributes;
663 if (!attributeFunction)
665 attributeFunction(script, string, items[i].pos, items[i].length, attributes);
670 enum _BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
671 typedef enum _BreakRule BreakRule ;
673 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
674 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
675 { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
676 { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
677 { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
678 { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
679 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
680 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
681 { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
682 { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
685 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
686 const HB_ScriptItem * items, hb_uint32 numItems,
687 HB_CharAttributes *attributes)
690 if (stringLength == 0)
692 unsigned int brk = HB_GetWordClass(string[0]);
693 attributes[0].wordBoundary = true;
694 for ( i = 1; i < stringLength; ++i) {
697 if (!attributes[i].charStop) {
698 attributes[i].wordBoundary = false;
701 nbrk = HB_GetWordClass(string[i]);
702 if (nbrk == HB_Word_Format) {
703 attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
706 rule = (BreakRule)wordbreakTable[brk][nbrk];
707 if (rule == Middle) {
709 hb_uint32 lookahead = i + 1;
710 while (lookahead < stringLength) {
711 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
712 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
716 if (testbrk == brk) {
718 while (i < lookahead)
719 attributes[i++].wordBoundary = false;
725 attributes[i].wordBoundary = (rule == Break);
731 enum SentenceBreakStates {
746 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
747 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
748 { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
749 { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
751 { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
752 { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
753 { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
754 { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
756 { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
757 { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
758 { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
759 { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
762 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
763 const HB_ScriptItem * items, hb_uint32 numItems,
764 HB_CharAttributes *attributes)
767 if (stringLength == 0)
769 hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
770 attributes[0].sentenceBoundary = true;
771 for (i = 1; i < stringLength; ++i) {
772 if (!attributes[i].charStop) {
773 attributes[i].sentenceBoundary = false;
776 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
777 if (brk == SB_Look) {
778 hb_uint32 lookahead = i + 1;
780 while (lookahead < stringLength) {
781 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
782 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
784 } else if (sbrk == HB_Sentence_Lower) {
790 if (brk == SB_Initial) {
791 while (i < lookahead)
792 attributes[i++].sentenceBoundary = false;
795 if (brk == SB_Break) {
796 attributes[i].sentenceBoundary = true;
797 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
799 attributes[i].sentenceBoundary = false;
805 static inline char *tag_to_string(HB_UInt tag)
807 static char string[5];
808 string[0] = (tag >> 24)&0xff;
809 string[1] = (tag >> 16)&0xff;
810 string[2] = (tag >> 8)&0xff;
811 string[3] = tag&0xff;
817 static void dump_string(HB_Buffer buffer)
819 for (uint i = 0; i < buffer->in_length; ++i) {
820 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
825 #define DEBUG if (1) ; else printf
828 #define DefaultLangSys 0xffff
829 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
841 typedef struct _OTScripts OTScripts;
843 static const OTScripts ot_scripts [] = {
845 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
847 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
849 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
851 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
853 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
855 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
857 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
859 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
861 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
863 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
865 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
867 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
869 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
871 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
873 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
875 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
877 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
879 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
881 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
883 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
885 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
887 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
889 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
891 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
893 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
895 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
897 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
899 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
901 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
903 static HB_Bool checkScript(HB_Face face, int script)
905 assert(script < HB_ScriptCount);
907 if (!face->gsub && !face->gpos)
910 unsigned int tag = ot_scripts[script].tag;
911 int requirements = ot_scripts[script].flags;
913 if (requirements & RequiresGsub) {
917 HB_UShort script_index;
918 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
920 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
921 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
927 if (requirements & RequiresGpos) {
931 HB_UShort script_index;
932 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
934 DEBUG("could not select script in gpos table: %d", error);
935 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
944 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
948 HB_Stream stream = 0;
953 error = tableFunc(font, tag, 0, &length);
956 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
959 stream->base = (HB_Byte*)malloc(length);
964 error = tableFunc(font, tag, stream->base, &length);
966 _hb_close_stream(stream);
969 stream->size = length;
971 stream->cursor = NULL;
975 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
978 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
981 HB_Stream gdefStream;
986 face->isSymbolFont = false;
990 face->current_script = HB_ScriptCount;
991 face->current_flags = HB_ShaperFlag_Default;
992 face->has_opentype_kerning = false;
993 face->tmpAttributes = 0;
994 face->tmpLogClusters = 0;
995 face->glyphs_substituted = false;
998 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
999 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
1000 //DEBUG("error loading gdef table: %d", error);
1004 //DEBUG() << "trying to load gsub table";
1005 stream = getTableStream(font, tableFunc, TTAG_GSUB);
1006 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
1008 if (error != HB_Err_Not_Covered) {
1009 //DEBUG("error loading gsub table: %d", error);
1011 //DEBUG("face doesn't have a gsub table");
1014 _hb_close_stream(stream);
1016 stream = getTableStream(font, tableFunc, TTAG_GPOS);
1017 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1019 DEBUG("error loading gpos table: %d", error);
1021 _hb_close_stream(stream);
1023 _hb_close_stream(gdefStream);
1025 for (i = 0; i < HB_ScriptCount; ++i)
1026 face->supported_scripts[i] = checkScript(face, i);
1028 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1036 void HB_FreeFace(HB_Face face)
1041 HB_Done_GPOS_Table(face->gpos);
1043 HB_Done_GSUB_Table(face->gsub);
1045 HB_Done_GDEF_Table(face->gdef);
1047 hb_buffer_free(face->buffer);
1048 if (face->tmpAttributes)
1049 free(face->tmpAttributes);
1050 if (face->tmpLogClusters)
1051 free(face->tmpLogClusters);
1055 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1057 HB_Script script = shaper_item->item.script;
1059 if (!shaper_item->face->supported_scripts[script])
1062 HB_Face face = shaper_item->face;
1063 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1066 face->current_script = script;
1067 face->current_flags = shaper_item->shaperFlags;
1069 assert(script < HB_ScriptCount);
1070 // find script in our list of supported scripts.
1071 unsigned int tag = ot_scripts[script].tag;
1073 if (face->gsub && features) {
1076 HB_FeatureList featurelist = face->gsub->FeatureList;
1077 int numfeatures = featurelist.FeatureCount;
1078 DEBUG("gsub table has %d features", numfeatures);
1079 for (int i = 0; i < numfeatures; i++) {
1080 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1081 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1085 HB_GSUB_Clear_Features(face->gsub);
1086 HB_UShort script_index;
1087 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1089 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1090 while (features->tag) {
1091 HB_UShort feature_index;
1092 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1094 DEBUG(" adding feature %s", tag_to_string(features->tag));
1095 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1103 face->has_opentype_kerning = false;
1106 HB_GPOS_Clear_Features(face->gpos);
1107 HB_UShort script_index;
1108 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1112 HB_FeatureList featurelist = face->gpos->FeatureList;
1113 int numfeatures = featurelist.FeatureCount;
1114 DEBUG("gpos table has %d features", numfeatures);
1115 for(int i = 0; i < numfeatures; i++) {
1116 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1117 HB_UShort feature_index;
1118 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1119 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1123 HB_UInt *feature_tag_list_buffer;
1124 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1126 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1127 while (*feature_tag_list) {
1128 HB_UShort feature_index;
1129 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1130 if (face->current_flags & HB_ShaperFlag_NoKerning) {
1134 face->has_opentype_kerning = true;
1136 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1138 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1141 FREE(feature_tag_list_buffer);
1149 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1151 HB_GlyphAttributes *tmpAttributes;
1152 unsigned int *tmpLogClusters;
1155 HB_Face face = item->face;
1157 face->length = item->num_glyphs;
1159 hb_buffer_clear(face->buffer);
1161 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1164 face->tmpAttributes = tmpAttributes;
1166 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1167 if (!tmpLogClusters)
1169 face->tmpLogClusters = tmpLogClusters;
1171 for (i = 0; i < face->length; ++i) {
1172 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1173 face->tmpAttributes[i] = item->attributes[i];
1174 face->tmpLogClusters[i] = item->log_clusters[i];
1178 DEBUG("-----------------------------------------");
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("original glyphs: %p", item->glyphs);
1183 for (int i = 0; i < length; ++i)
1184 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1185 // dump_string(hb_buffer);
1188 face->glyphs_substituted = false;
1190 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1191 if (error && error != HB_Err_Not_Covered)
1193 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1197 // DEBUG("log clusters before shaping:");
1198 // for (int j = 0; j < length; j++)
1199 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1200 DEBUG("shaped glyphs:");
1201 for (int i = 0; i < length; ++i)
1202 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1203 DEBUG("-----------------------------------------");
1204 // dump_string(hb_buffer);
1210 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1212 HB_Face face = item->face;
1215 HB_Glyph *glyphs = item->glyphs;
1216 HB_GlyphAttributes *attributes = item->attributes;
1218 bool glyphs_positioned = false;
1220 if (face->buffer->positions)
1221 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1222 // #### check that passing "false,false" is correct
1223 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1226 if (!face->glyphs_substituted && !glyphs_positioned) {
1227 HB_GetGlyphAdvances(item);
1228 return true; // nothing to do for us
1231 // make sure we have enough space to write everything back
1232 if (availableGlyphs < (int)face->buffer->in_length) {
1233 item->num_glyphs = face->buffer->in_length;
1238 for (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) {
1249 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1250 unsigned short *logClusters = item->log_clusters;
1251 int clusterStart = 0;
1253 // #### the reconstruction of the logclusters currently does not work if the original string
1254 // contains surrogate pairs
1255 for (i = 0; i < face->buffer->in_length; ++i) {
1256 int ci = face->buffer->in_string[i].cluster;
1257 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1258 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1259 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1260 for ( j = oldCi; j < ci; j++)
1261 logClusters[j] = clusterStart;
1266 for (j = oldCi; j < face->length; j++)
1267 logClusters[j] = clusterStart;
1270 // calulate the advances for the shaped glyphs
1271 // DEBUG("unpositioned: ");
1273 // positioning code:
1274 if (glyphs_positioned) {
1276 HB_Position positions = face->buffer->positions;
1277 HB_Fixed *advances = item->advances;
1278 HB_GetGlyphAdvances(item);
1280 // DEBUG("positioned glyphs:");
1281 for (i = 0; i < face->buffer->in_length; i++) {
1282 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1283 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1284 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1285 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1286 // positions[i].back, positions[i].new_advance);
1288 HB_Fixed adjustment = (item->item.bidiLevel % 2) ? -positions[i].x_advance : positions[i].x_advance;
1290 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1291 adjustment = HB_FIXED_ROUND(adjustment);
1293 if (positions[i].new_advance) {
1294 advances[i] = adjustment;
1296 advances[i] += adjustment;
1300 HB_FixedPoint *offsets = item->offsets;
1301 offsets[i].x = positions[i].x_pos;
1302 offsets[i].y = positions[i].y_pos;
1303 while (positions[i - back].back) {
1304 back += positions[i - back].back;
1305 offsets[i].x += positions[i - back].x_pos;
1306 offsets[i].y += positions[i - back].y_pos;
1308 offsets[i].y = -offsets[i].y;
1310 if (item->item.bidiLevel % 2) {
1311 // ### may need to go back multiple glyphs like in ltr
1312 back = positions[i].back;
1314 offsets[i].x -= advances[i-back];
1317 while (positions[i - back].back) {
1318 back += positions[i - back].back;
1319 offsets[i].x -= advances[i-back];
1322 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1323 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1325 item->kerning_applied = face->has_opentype_kerning;
1327 HB_HeuristicPosition(item);
1331 if (doLogClusters) {
1332 DEBUG("log clusters after shaping:");
1333 for (int j = 0; j < length; j++)
1334 DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1336 DEBUG("final glyphs:");
1337 for (int i = 0; i < (int)hb_buffer->in_length; ++i)
1338 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
1339 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
1340 glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
1341 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1342 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1343 DEBUG("-----------------------------------------");
1348 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1350 HB_Bool result = false;
1351 if (shaper_item->num_glyphs < shaper_item->item.length) {
1352 shaper_item->num_glyphs = shaper_item->item.length;
1355 assert(shaper_item->item.script < HB_ScriptCount);
1356 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1357 shaper_item->glyphIndicesPresent = false;