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)
188 // for south east asian chars that require a complex (dictionary analysis), the unicode
189 // standard recommends to treat them as AL. thai_attributes and other attribute methods that
190 // do dictionary analysis can override
191 if (tcls >= HB_LineBreak_SA)
192 tcls = HB_LineBreak_AL;
193 if (cls >= HB_LineBreak_SA)
194 cls = HB_LineBreak_AL;
196 int brk = breakTable[cls][tcls];
199 lineBreakType = HB_Break;
200 if (uc[i-1] == 0xad) // soft hyphen
201 lineBreakType = HB_SoftHyphen;
204 lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
206 case CombiningIndirectBreak:
207 lineBreakType = HB_NoBreak;
208 if (lcls == HB_LineBreak_SP){
210 charAttributes[i-2].lineBreakType = HB_Break;
212 goto next_no_cls_update;
215 case CombiningProhibitedBreak:
216 lineBreakType = HB_NoBreak;
217 if (lcls != HB_LineBreak_SP)
218 goto next_no_cls_update;
219 case ProhibitedBreak:
228 grapheme = ngrapheme;
229 charAttributes[i-1].lineBreakType = lineBreakType;
231 charAttributes[len-1].lineBreakType = HB_ForcedBreak;
234 // --------------------------------------------------------------------------------------------------------------------------------------------
238 // --------------------------------------------------------------------------------------------------------------------------------------------
240 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
242 int nmarks = glast - gfrom;
245 HB_Glyph *glyphs = item->glyphs;
246 HB_GlyphAttributes *attributes = item->attributes;
248 HB_GlyphMetrics baseMetrics;
249 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
251 if (item->item.script == HB_Script_Hebrew
252 && (-baseMetrics.y) > baseMetrics.height)
253 // we need to attach below the baseline, because of the hebrew iud.
254 baseMetrics.height = -baseMetrics.y;
256 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
257 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
259 HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
260 HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
261 if (size > HB_FIXED_CONSTANT(4))
262 offsetBase += HB_FIXED_CONSTANT(4);
265 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
266 // qDebug("offset = %f", offsetBase);
268 bool rightToLeft = item->item.bidiLevel % 2;
271 unsigned char lastCmb = 0;
272 HB_GlyphMetrics attachmentRect;
273 memset(&attachmentRect, 0, sizeof(attachmentRect));
275 for(i = 1; i <= nmarks; i++) {
276 HB_Glyph mark = glyphs[gfrom+i];
277 HB_GlyphMetrics markMetrics;
278 item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
281 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
283 HB_Fixed offset = offsetBase;
284 unsigned char cmb = attributes[gfrom+i].combiningClass;
286 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
287 // bits in the glyphAttributes structure.
289 // fixed position classes. We approximate by mapping to one of the others.
290 // currently I added only the ones for arabic, hebrew, lao and thai.
292 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
294 // add a bit more offset to arabic, a bit hacky
295 if (cmb >= 27 && cmb <= 36 && offset < 3)
298 if ((cmb >= 10 && cmb <= 18) ||
299 cmb == 20 || cmb == 22 ||
300 cmb == 29 || cmb == 32)
301 cmb = HB_Combining_Below;
303 else if (cmb == 23 || cmb == 27 || cmb == 28 ||
304 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
305 cmb = HB_Combining_Above;
307 else if (cmb == 9 || cmb == 103 || cmb == 118)
308 cmb = HB_Combining_BelowRight;
310 else if (cmb == 24 || cmb == 107 || cmb == 122)
311 cmb = HB_Combining_AboveRight;
313 cmb = HB_Combining_AboveLeft;
319 // combining marks of different class don't interact. Reset the rectangle.
320 if (cmb != lastCmb) {
321 //qDebug("resetting rect");
322 attachmentRect = baseMetrics;
326 case HB_Combining_DoubleBelow:
327 // ### wrong in rtl context!
328 case HB_Combining_BelowLeft:
330 case HB_Combining_BelowLeftAttached:
331 p.x += attachmentRect.x - markMetrics.x;
332 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
334 case HB_Combining_Below:
336 case HB_Combining_BelowAttached:
337 p.x += attachmentRect.x - markMetrics.x;
338 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
340 p.x += (attachmentRect.width - markMetrics.width) / 2;
342 case HB_Combining_BelowRight:
344 case HB_Combining_BelowRightAttached:
345 p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
346 p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
348 case HB_Combining_Left:
350 case HB_Combining_LeftAttached:
352 case HB_Combining_Right:
354 case HB_Combining_RightAttached:
356 case HB_Combining_DoubleAbove:
357 // ### wrong in RTL context!
358 case HB_Combining_AboveLeft:
360 case HB_Combining_AboveLeftAttached:
361 p.x += attachmentRect.x - markMetrics.x;
362 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
364 case HB_Combining_Above:
366 case HB_Combining_AboveAttached:
367 p.x += attachmentRect.x - markMetrics.x;
368 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
370 p.x += (attachmentRect.width - markMetrics.width) / 2;
372 case HB_Combining_AboveRight:
374 case HB_Combining_AboveRightAttached:
375 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
376 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
379 case HB_Combining_IotaSubscript:
383 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
384 markMetrics.x += p.x;
385 markMetrics.y += p.y;
387 HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
388 unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
389 unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
390 unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
391 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
392 attachmentRect = unitedAttachmentRect;
396 item->offsets[gfrom+i].x = p.x;
397 item->offsets[gfrom+i].y = p.y;
399 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
400 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
402 item->advances[gfrom+i] = 0;
406 void HB_HeuristicPosition(HB_ShaperItem *item)
408 HB_GetGlyphAdvances(item);
409 HB_GlyphAttributes *attributes = item->attributes;
412 int i = item->num_glyphs;
414 if (cEnd == -1 && attributes[i].mark) {
416 } else if (cEnd != -1 && !attributes[i].mark) {
417 positionCluster(item, i, cEnd);
423 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
424 // and no reordering.
425 // also computes logClusters heuristically
426 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
428 const HB_UChar16 *uc = item->string + item->item.pos;
429 hb_uint32 length = item->item.length;
431 // ### zeroWidth and justification are missing here!!!!!
433 assert(item->num_glyphs <= length);
435 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
436 HB_GlyphAttributes *attributes = item->attributes;
437 unsigned short *logClusters = item->log_clusters;
439 hb_uint32 glyph_pos = 0;
441 for (i = 0; i < length; i++) {
442 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
443 && HB_IsLowSurrogate(uc[i + 1])) {
444 logClusters[i] = glyph_pos;
445 logClusters[++i] = glyph_pos;
447 logClusters[i] = glyph_pos;
451 assert(glyph_pos == item->num_glyphs);
453 // first char in a run is never (treated as) a mark
455 const bool symbolFont = item->face->isSymbolFont;
456 attributes[0].mark = false;
457 attributes[0].clusterStart = true;
458 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
461 HB_CharCategory lastCat;
463 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
464 for (i = 1; i < length; ++i) {
465 if (logClusters[i] == pos)
469 while (pos < logClusters[i]) {
470 attributes[pos] = attributes[pos-1];
473 // hide soft-hyphens by default
474 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
475 attributes[pos].dontPrint = true;
478 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
479 if (cat != HB_Mark_NonSpacing) {
480 attributes[pos].mark = false;
481 attributes[pos].clusterStart = true;
482 attributes[pos].combiningClass = 0;
483 cStart = logClusters[i];
486 // Fix 0 combining classes
487 if ((uc[pos] & 0xff00) == 0x0e00) {
489 if (uc[pos] == 0xe31 ||
498 cmb = HB_Combining_AboveRight;
499 } else if (uc[pos] == 0xeb1 ||
507 cmb = HB_Combining_Above;
508 } else if (uc[pos] == 0xebc) {
509 cmb = HB_Combining_Below;
514 attributes[pos].mark = true;
515 attributes[pos].clusterStart = false;
516 attributes[pos].combiningClass = cmb;
517 logClusters[i] = cStart;
519 // one gets an inter character justification point if the current char is not a non spacing mark.
520 // as then the current char belongs to the last one and one gets a space justification point
521 // after the space char.
522 if (lastCat == HB_Separator_Space)
523 attributes[pos-1].justification = HB_Space;
524 else if (cat != HB_Mark_NonSpacing)
525 attributes[pos-1].justification = HB_Character;
527 attributes[pos-1].justification = HB_NoJustification;
531 pos = logClusters[length-1];
532 if (lastCat == HB_Separator_Space)
533 attributes[pos].justification = HB_Space;
535 attributes[pos].justification = HB_Character;
539 static const HB_OpenTypeFeature basic_features[] = {
540 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
541 { HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
542 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CligProperty },
546 static const HB_OpenTypeFeature disabled_features[] = {
547 { HB_MAKE_TAG('c', 'p', 'c', 't'), PositioningProperties },
548 { HB_MAKE_TAG('h', 'a', 'l', 't'), PositioningProperties },
549 // TODO: we need to add certain HB_ShaperFlag for vertical
550 // writing mode to enable these vertical writing features:
551 { HB_MAKE_TAG('v', 'a', 'l', 't'), PositioningProperties },
552 { HB_MAKE_TAG('v', 'h', 'a', 'l'), PositioningProperties },
553 { HB_MAKE_TAG('v', 'k', 'r', 'n'), PositioningProperties },
554 { HB_MAKE_TAG('v', 'p', 'a', 'l'), PositioningProperties },
559 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
561 if (shaper_item->glyphIndicesPresent) {
562 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
563 shaper_item->glyphIndicesPresent = false;
566 return shaper_item->font->klass
567 ->convertStringToGlyphIndices(shaper_item->font,
568 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
569 shaper_item->glyphs, &shaper_item->num_glyphs,
570 shaper_item->item.bidiLevel % 2);
573 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
576 const int availableGlyphs = shaper_item->num_glyphs;
579 if (!HB_ConvertStringToGlyphIndices(shaper_item))
582 HB_HeuristicSetGlyphAttributes(shaper_item);
585 if (HB_SelectScript(shaper_item, basic_features)) {
586 HB_OpenTypeShape(shaper_item, /*properties*/0);
587 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
591 HB_HeuristicPosition(shaper_item);
595 const HB_ScriptEngine HB_ScriptEngines[] = {
605 { HB_HebrewShape, 0 },
607 { HB_ArabicShape, 0},
609 { HB_ArabicShape, 0},
611 { HB_BasicShape, 0 },
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_IndicShape, HB_IndicAttributes },
631 { HB_IndicShape, HB_IndicAttributes },
633 { HB_ThaiShape, HB_ThaiAttributes },
635 { HB_BasicShape, 0 },
637 { HB_TibetanShape, HB_TibetanAttributes },
639 { HB_MyanmarShape, HB_MyanmarAttributes },
641 { HB_BasicShape, 0 },
643 { HB_HangulShape, 0 },
645 { HB_BasicShape, 0 },
647 { HB_BasicShape, 0 },
649 { HB_KhmerShape, HB_KhmerAttributes },
654 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
655 const HB_ScriptItem *items, hb_uint32 numItems,
656 HB_CharAttributes *attributes)
658 memset(attributes, 0, stringLength * sizeof(HB_CharAttributes));
659 calcLineBreaks(string, stringLength, attributes);
661 for (hb_uint32 i = 0; i < numItems; ++i) {
662 HB_Script script = items[i].script;
663 if (script == HB_Script_Inherited)
664 script = HB_Script_Common;
665 HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
666 if (!attributeFunction)
668 attributeFunction(script, string, items[i].pos, items[i].length, attributes);
673 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
675 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
676 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
677 { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
678 { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
679 { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
680 { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
681 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
682 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
683 { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
684 { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
687 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
688 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
689 HB_CharAttributes *attributes)
691 if (stringLength == 0)
693 unsigned int brk = HB_GetWordClass(string[0]);
694 attributes[0].wordBoundary = true;
695 for (hb_uint32 i = 1; i < stringLength; ++i) {
696 if (!attributes[i].charStop) {
697 attributes[i].wordBoundary = false;
700 hb_uint32 nbrk = HB_GetWordClass(string[i]);
701 if (nbrk == HB_Word_Format) {
702 attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
705 BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
706 if (rule == Middle) {
708 hb_uint32 lookahead = i + 1;
709 while (lookahead < stringLength) {
710 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
711 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
715 if (testbrk == brk) {
717 while (i < lookahead)
718 attributes[i++].wordBoundary = false;
724 attributes[i].wordBoundary = (rule == Break);
730 enum SentenceBreakStates {
745 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
746 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
747 { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
748 { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
750 { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
751 { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
752 { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
753 { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
755 { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
756 { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
757 { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
758 { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
761 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
762 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
763 HB_CharAttributes *attributes)
765 if (stringLength == 0)
767 hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
768 attributes[0].sentenceBoundary = true;
769 for (hb_uint32 i = 1; i < stringLength; ++i) {
770 if (!attributes[i].charStop) {
771 attributes[i].sentenceBoundary = false;
774 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
775 if (brk == SB_Look) {
777 hb_uint32 lookahead = i + 1;
778 while (lookahead < stringLength) {
779 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
780 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
782 } else if (sbrk == HB_Sentence_Lower) {
788 if (brk == SB_Initial) {
789 while (i < lookahead)
790 attributes[i++].sentenceBoundary = false;
793 if (brk == SB_Break) {
794 attributes[i].sentenceBoundary = true;
795 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
797 attributes[i].sentenceBoundary = false;
803 static inline char *tag_to_string(HB_UInt tag)
805 static char string[5];
806 string[0] = (tag >> 24)&0xff;
807 string[1] = (tag >> 16)&0xff;
808 string[2] = (tag >> 8)&0xff;
809 string[3] = tag&0xff;
815 static void dump_string(HB_Buffer buffer)
817 for (uint i = 0; i < buffer->in_length; ++i) {
818 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
823 #define DEBUG if (1) ; else printf
826 #define DefaultLangSys 0xffff
827 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
838 static const OTScripts ot_scripts [] = {
840 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
842 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
844 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
846 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
848 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
850 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
852 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
854 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
856 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
858 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
860 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
862 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
864 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
866 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
868 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
870 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
872 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
874 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
876 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
878 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
880 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
882 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
884 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
886 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
888 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
890 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
892 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
894 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
896 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
898 static HB_Bool checkScript(HB_Face face, int script)
900 assert(script < HB_ScriptCount);
902 if (!face->gsub && !face->gpos)
905 unsigned int tag = ot_scripts[script].tag;
906 int requirements = ot_scripts[script].flags;
908 if (requirements & RequiresGsub) {
912 HB_UShort script_index;
913 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
915 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
916 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
922 if (requirements & RequiresGpos) {
926 HB_UShort script_index;
927 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
929 DEBUG("could not select script in gpos table: %d", error);
930 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
939 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
943 HB_Stream stream = 0;
948 error = tableFunc(font, tag, 0, &length);
951 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
954 stream->base = (HB_Byte*)malloc(length);
959 error = tableFunc(font, tag, stream->base, &length);
961 _hb_close_stream(stream);
964 stream->size = length;
966 stream->cursor = NULL;
970 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
972 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
976 face->isSymbolFont = false;
980 face->current_script = HB_ScriptCount;
981 face->current_flags = HB_ShaperFlag_Default;
982 face->has_opentype_kerning = false;
983 face->tmpAttributes = 0;
984 face->tmpLogClusters = 0;
985 face->glyphs_substituted = false;
988 HB_Error error = HB_Err_Ok;
990 HB_Stream gdefStream;
992 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
993 error = HB_Err_Not_Covered;
994 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
995 //DEBUG("error loading gdef table: %d", error);
999 //DEBUG() << "trying to load gsub table";
1000 stream = getTableStream(font, tableFunc, TTAG_GSUB);
1001 error = HB_Err_Not_Covered;
1002 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
1004 if (error != HB_Err_Not_Covered) {
1005 //DEBUG("error loading gsub table: %d", error);
1007 //DEBUG("face doesn't have a gsub table");
1010 _hb_close_stream(stream);
1012 stream = getTableStream(font, tableFunc, TTAG_GPOS);
1013 error = HB_Err_Not_Covered;
1014 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1016 DEBUG("error loading gpos table: %d", error);
1018 _hb_close_stream(stream);
1020 _hb_close_stream(gdefStream);
1022 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1023 face->supported_scripts[i] = checkScript(face, i);
1025 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1033 void HB_FreeFace(HB_Face face)
1038 HB_Done_GPOS_Table(face->gpos);
1040 HB_Done_GSUB_Table(face->gsub);
1042 HB_Done_GDEF_Table(face->gdef);
1044 hb_buffer_free(face->buffer);
1045 if (face->tmpAttributes)
1046 free(face->tmpAttributes);
1047 if (face->tmpLogClusters)
1048 free(face->tmpLogClusters);
1052 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1054 HB_Script script = shaper_item->item.script;
1056 HB_Face face = shaper_item->face;
1057 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1058 return shaper_item->face->supported_scripts[script] ? true : false;
1060 face->current_script = script;
1061 face->current_flags = shaper_item->shaperFlags;
1063 if (!shaper_item->face->supported_scripts[script])
1066 assert(script < HB_ScriptCount);
1067 // find script in our list of supported scripts.
1068 unsigned int tag = ot_scripts[script].tag;
1070 if (face->gsub && features) {
1073 HB_FeatureList featurelist = face->gsub->FeatureList;
1074 int numfeatures = featurelist.FeatureCount;
1075 DEBUG("gsub table has %d features", numfeatures);
1076 for (int i = 0; i < numfeatures; i++) {
1077 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1078 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1082 HB_GSUB_Clear_Features(face->gsub);
1083 HB_UShort script_index;
1084 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1086 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1087 while (features->tag) {
1088 HB_UShort feature_index;
1089 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1091 DEBUG(" adding feature %s", tag_to_string(features->tag));
1092 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1100 face->has_opentype_kerning = false;
1103 HB_GPOS_Clear_Features(face->gpos);
1104 HB_UShort script_index;
1105 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1109 HB_FeatureList featurelist = face->gpos->FeatureList;
1110 int numfeatures = featurelist.FeatureCount;
1111 DEBUG("gpos table has %d features", numfeatures);
1112 for(int i = 0; i < numfeatures; i++) {
1113 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1114 HB_UShort feature_index;
1115 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1116 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1120 HB_UInt *feature_tag_list_buffer;
1121 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1123 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1124 while (*feature_tag_list) {
1125 HB_UShort feature_index;
1127 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1128 if (face->current_flags & HB_ShaperFlag_NoKerning)
1131 face->has_opentype_kerning = true;
1133 features = disabled_features;
1134 while (features->tag) {
1135 if (*feature_tag_list == features->tag) {
1141 // 'palt' should be turned off by default unless 'kern' is on
1142 if (!face->has_opentype_kerning &&
1143 *feature_tag_list == HB_MAKE_TAG('p', 'a', 'l', 't'))
1150 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1152 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1155 FREE(feature_tag_list_buffer);
1163 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1165 HB_GlyphAttributes *tmpAttributes;
1166 unsigned int *tmpLogClusters;
1168 HB_Face face = item->face;
1170 face->length = item->num_glyphs;
1172 hb_buffer_clear(face->buffer);
1174 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1177 face->tmpAttributes = tmpAttributes;
1179 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1180 if (!tmpLogClusters)
1182 face->tmpLogClusters = tmpLogClusters;
1184 for (int i = 0; i < face->length; ++i) {
1185 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1186 face->tmpAttributes[i] = item->attributes[i];
1187 face->tmpLogClusters[i] = item->log_clusters[i];
1191 DEBUG("-----------------------------------------");
1192 // DEBUG("log clusters before shaping:");
1193 // for (int j = 0; j < length; j++)
1194 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1195 DEBUG("original glyphs: %p", item->glyphs);
1196 for (int i = 0; i < length; ++i)
1197 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1198 // dump_string(hb_buffer);
1201 face->glyphs_substituted = false;
1203 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1204 if (error && error != HB_Err_Not_Covered)
1206 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1210 // DEBUG("log clusters before shaping:");
1211 // for (int j = 0; j < length; j++)
1212 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1213 DEBUG("shaped glyphs:");
1214 for (int i = 0; i < length; ++i)
1215 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1216 DEBUG("-----------------------------------------");
1217 // dump_string(hb_buffer);
1223 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1225 HB_Face face = item->face;
1227 bool glyphs_positioned = false;
1229 if (face->buffer->positions)
1230 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1231 // #### check that passing "false,false" is correct
1232 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1235 if (!face->glyphs_substituted && !glyphs_positioned) {
1236 HB_HeuristicPosition(item);
1237 return true; // nothing to do for us
1240 // make sure we have enough space to write everything back
1241 if (availableGlyphs < (int)face->buffer->in_length) {
1242 item->num_glyphs = face->buffer->in_length;
1246 HB_Glyph *glyphs = item->glyphs;
1247 HB_GlyphAttributes *attributes = item->attributes;
1249 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1250 glyphs[i] = face->buffer->in_string[i].gindex;
1251 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
1252 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
1253 attributes[i].clusterStart = false;
1255 item->num_glyphs = face->buffer->in_length;
1257 if (doLogClusters && face->glyphs_substituted) {
1258 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1259 unsigned short *logClusters = item->log_clusters;
1260 int clusterStart = 0;
1262 // #### the reconstruction of the logclusters currently does not work if the original string
1263 // contains surrogate pairs
1264 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1265 int ci = face->buffer->in_string[i].cluster;
1266 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1267 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1268 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1269 for (int j = oldCi; j < ci; j++)
1270 logClusters[j] = clusterStart;
1275 for (int j = oldCi; j < face->length; j++)
1276 logClusters[j] = clusterStart;
1279 // calulate the advances for the shaped glyphs
1280 // DEBUG("unpositioned: ");
1282 // positioning code:
1283 if (glyphs_positioned) {
1284 HB_GetGlyphAdvances(item);
1285 HB_Position positions = face->buffer->positions;
1286 HB_Fixed *advances = item->advances;
1288 // DEBUG("positioned glyphs:");
1289 for (unsigned int i = 0; i < face->buffer->in_length; i++) {
1290 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1291 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1292 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1293 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1294 // positions[i].back, positions[i].new_advance);
1296 HB_Fixed adjustment = positions[i].x_advance;
1298 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1299 adjustment = HB_FIXED_ROUND(adjustment);
1301 if (positions[i].new_advance) {
1302 advances[i] = adjustment;
1304 advances[i] += adjustment;
1308 HB_FixedPoint *offsets = item->offsets;
1309 offsets[i].x = positions[i].x_pos;
1310 offsets[i].y = positions[i].y_pos;
1311 while (positions[i - back].back) {
1312 back += positions[i - back].back;
1313 offsets[i].x += positions[i - back].x_pos;
1314 offsets[i].y += positions[i - back].y_pos;
1316 offsets[i].y = -offsets[i].y;
1318 if (item->item.bidiLevel % 2) {
1319 // ### may need to go back multiple glyphs like in ltr
1320 back = positions[i].back;
1322 offsets[i].x -= advances[i-back];
1325 while (positions[i - back].back) {
1326 back += positions[i - back].back;
1327 offsets[i].x -= advances[i-back];
1330 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1331 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1333 item->kerning_applied = face->has_opentype_kerning;
1335 HB_HeuristicPosition(item);
1339 if (doLogClusters) {
1340 DEBUG("log clusters after shaping:\n");
1341 for (unsigned int j = 0; j < item->item.length; j++)
1342 DEBUG(" log[%d] = %d\n", j, item->log_clusters[j]);
1344 DEBUG("final glyphs:\n");
1345 for (unsigned int i = 0; i < item->num_glyphs; ++i)
1346 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d offset=%d/%d\n",
1347 glyphs[i], face->buffer->in_string[i].cluster, attributes[i].mark,
1348 attributes[i].combiningClass, attributes[i].clusterStart,
1349 item->advances[i] >> 6,
1350 item->offsets[i].x >> 6, item->offsets[i].y >> 6);
1351 DEBUG("-----------------------------------------\n");
1356 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1358 HB_Bool result = false;
1359 if (shaper_item->num_glyphs < shaper_item->item.length) {
1360 shaper_item->num_glyphs = shaper_item->item.length;
1363 assert(shaper_item->item.script < HB_ScriptCount);
1364 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1365 shaper_item->glyphIndicesPresent = false;