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 // To fix some Thai character heights check for two above glyphs
269 if (nmarks == 2 && (attributes[gfrom+1].combiningClass == HB_Combining_AboveRight ||
270 attributes[gfrom+1].combiningClass == HB_Combining_AboveLeft ||
271 attributes[gfrom+1].combiningClass == HB_Combining_Above))
272 if (attributes[gfrom+2].combiningClass == 23 ||
273 attributes[gfrom+2].combiningClass == 24 ||
274 attributes[gfrom+2].combiningClass == 25 ||
275 attributes[gfrom+2].combiningClass == 27 ||
276 attributes[gfrom+2].combiningClass == 28 ||
277 attributes[gfrom+2].combiningClass == 30 ||
278 attributes[gfrom+2].combiningClass == 31 ||
279 attributes[gfrom+2].combiningClass == 33 ||
280 attributes[gfrom+2].combiningClass == 34 ||
281 attributes[gfrom+2].combiningClass == 35 ||
282 attributes[gfrom+2].combiningClass == 36 ||
283 attributes[gfrom+2].combiningClass == 107 ||
284 attributes[gfrom+2].combiningClass == 122) {
285 // Two above glyphs, check total height
286 int markTotalHeight = baseMetrics.height;
287 HB_GlyphMetrics markMetrics;
288 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom+1], &markMetrics);
289 markTotalHeight += markMetrics.height;
290 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom+2], &markMetrics);
291 markTotalHeight += markMetrics.height;
292 if ((markTotalHeight + 2 * offsetBase) > (size * 10))
293 offsetBase = ((size * 10) - markTotalHeight) / 2; // Use offset that just fits
296 bool rightToLeft = item->item.bidiLevel % 2;
299 unsigned char lastCmb = 0;
300 HB_GlyphMetrics attachmentRect;
301 memset(&attachmentRect, 0, sizeof(attachmentRect));
303 for(i = 1; i <= nmarks; i++) {
304 HB_Glyph mark = glyphs[gfrom+i];
305 HB_GlyphMetrics markMetrics;
306 item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
309 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
311 HB_Fixed offset = offsetBase;
312 unsigned char cmb = attributes[gfrom+i].combiningClass;
314 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
315 // bits in the glyphAttributes structure.
317 // fixed position classes. We approximate by mapping to one of the others.
318 // currently I added only the ones for arabic, hebrew, lao and thai.
320 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
322 // add a bit more offset to arabic, a bit hacky
323 if (cmb >= 27 && cmb <= 36 && offset < 3)
326 if ((cmb >= 10 && cmb <= 18) ||
327 cmb == 20 || cmb == 22 ||
328 cmb == 29 || cmb == 32)
329 cmb = HB_Combining_Below;
331 else if (cmb == 23 || cmb == 27 || cmb == 28 ||
332 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
333 cmb = HB_Combining_Above;
335 else if (cmb == 9 || cmb == 103 || cmb == 118)
336 cmb = HB_Combining_BelowRight;
338 else if (cmb == 24 || cmb == 107 || cmb == 122)
339 cmb = HB_Combining_AboveRight;
341 cmb = HB_Combining_AboveLeft;
347 // combining marks of different class don't interact. Reset the rectangle.
348 if (cmb != lastCmb) {
349 //qDebug("resetting rect");
350 attachmentRect = baseMetrics;
354 case HB_Combining_DoubleBelow:
355 // ### wrong in rtl context!
356 case HB_Combining_BelowLeft:
358 case HB_Combining_BelowLeftAttached:
359 p.x += attachmentRect.x - markMetrics.x;
360 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
362 case HB_Combining_Below:
364 case HB_Combining_BelowAttached:
365 p.x += attachmentRect.x - markMetrics.x;
366 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
368 p.x += (attachmentRect.width - markMetrics.width) / 2;
370 case HB_Combining_BelowRight:
372 case HB_Combining_BelowRightAttached:
373 p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
374 p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
376 case HB_Combining_Left:
378 case HB_Combining_LeftAttached:
380 case HB_Combining_Right:
382 case HB_Combining_RightAttached:
384 case HB_Combining_DoubleAbove:
385 // ### wrong in RTL context!
386 case HB_Combining_AboveLeft:
388 case HB_Combining_AboveLeftAttached:
389 p.x += attachmentRect.x - markMetrics.x;
390 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
392 case HB_Combining_Above:
394 case HB_Combining_AboveAttached:
395 p.x += attachmentRect.x - markMetrics.x;
396 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
398 p.x += (attachmentRect.width - markMetrics.width) / 2;
400 case HB_Combining_AboveRight:
402 case HB_Combining_AboveRightAttached:
403 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
404 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
407 case HB_Combining_IotaSubscript:
411 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
412 markMetrics.x += p.x;
413 markMetrics.y += p.y;
415 HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
416 unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
417 unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
418 unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
419 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
420 attachmentRect = unitedAttachmentRect;
424 item->offsets[gfrom+i].x = p.x;
425 item->offsets[gfrom+i].y = p.y;
427 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
428 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
430 item->advances[gfrom+i] = 0;
434 void HB_HeuristicPosition(HB_ShaperItem *item)
436 HB_GetGlyphAdvances(item);
437 HB_GlyphAttributes *attributes = item->attributes;
440 int i = item->num_glyphs;
442 if (cEnd == -1 && attributes[i].mark) {
444 } else if (cEnd != -1 && !attributes[i].mark) {
445 positionCluster(item, i, cEnd);
451 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
452 // and no reordering.
453 // also computes logClusters heuristically
454 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
456 const HB_UChar16 *uc = item->string + item->item.pos;
457 hb_uint32 length = item->item.length;
459 // ### zeroWidth and justification are missing here!!!!!
461 assert(item->num_glyphs <= length);
463 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
464 HB_GlyphAttributes *attributes = item->attributes;
465 unsigned short *logClusters = item->log_clusters;
467 hb_uint32 glyph_pos = 0;
469 for (i = 0; i < length; i++) {
470 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
471 && HB_IsLowSurrogate(uc[i + 1])) {
472 logClusters[i] = glyph_pos;
473 logClusters[++i] = glyph_pos;
475 logClusters[i] = glyph_pos;
479 assert(glyph_pos == item->num_glyphs);
481 // first char in a run is never (treated as) a mark
483 const bool symbolFont = item->face->isSymbolFont;
484 attributes[0].mark = false;
485 attributes[0].clusterStart = true;
486 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
489 HB_CharCategory lastCat;
491 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
492 for (i = 1; i < length; ++i) {
493 if (logClusters[i] == pos)
497 while (pos < logClusters[i]) {
498 attributes[pos] = attributes[pos-1];
501 // hide soft-hyphens by default
502 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
503 attributes[pos].dontPrint = true;
506 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
507 if (cat != HB_Mark_NonSpacing) {
508 attributes[pos].mark = false;
509 attributes[pos].clusterStart = true;
510 attributes[pos].combiningClass = 0;
511 cStart = logClusters[i];
514 // Fix 0 combining classes
515 if ((uc[pos] & 0xff00) == 0x0e00) {
517 if (uc[pos] == 0xe31 ||
526 cmb = HB_Combining_AboveRight;
527 } else if (uc[pos] == 0xeb1 ||
535 cmb = HB_Combining_Above;
536 } else if (uc[pos] == 0xebc) {
537 cmb = HB_Combining_Below;
542 attributes[pos].mark = true;
543 attributes[pos].clusterStart = false;
544 attributes[pos].combiningClass = cmb;
545 logClusters[i] = cStart;
547 // one gets an inter character justification point if the current char is not a non spacing mark.
548 // as then the current char belongs to the last one and one gets a space justification point
549 // after the space char.
550 if (lastCat == HB_Separator_Space)
551 attributes[pos-1].justification = HB_Space;
552 else if (cat != HB_Mark_NonSpacing)
553 attributes[pos-1].justification = HB_Character;
555 attributes[pos-1].justification = HB_NoJustification;
559 pos = logClusters[length-1];
560 if (lastCat == HB_Separator_Space)
561 attributes[pos].justification = HB_Space;
563 attributes[pos].justification = HB_Character;
567 static const HB_OpenTypeFeature basic_features[] = {
568 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
569 { HB_MAKE_TAG('l', 'i', 'g', 'a'), LigaProperty },
570 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CligProperty },
574 static const HB_OpenTypeFeature disabled_features[] = {
575 { HB_MAKE_TAG('c', 'p', 'c', 't'), PositioningProperties },
576 { HB_MAKE_TAG('h', 'a', 'l', 't'), PositioningProperties },
577 // TODO: we need to add certain HB_ShaperFlag for vertical
578 // writing mode to enable these vertical writing features:
579 { HB_MAKE_TAG('v', 'a', 'l', 't'), PositioningProperties },
580 { HB_MAKE_TAG('v', 'h', 'a', 'l'), PositioningProperties },
581 { HB_MAKE_TAG('v', 'k', 'r', 'n'), PositioningProperties },
582 { HB_MAKE_TAG('v', 'p', 'a', 'l'), PositioningProperties },
587 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
589 if (shaper_item->glyphIndicesPresent) {
590 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
591 shaper_item->glyphIndicesPresent = false;
594 return shaper_item->font->klass
595 ->convertStringToGlyphIndices(shaper_item->font,
596 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
597 shaper_item->glyphs, &shaper_item->num_glyphs,
598 shaper_item->item.bidiLevel % 2);
601 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
604 const int availableGlyphs = shaper_item->num_glyphs;
607 if (!HB_ConvertStringToGlyphIndices(shaper_item))
610 HB_HeuristicSetGlyphAttributes(shaper_item);
613 if (HB_SelectScript(shaper_item, basic_features)) {
614 HB_OpenTypeShape(shaper_item, /*properties*/0);
615 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
619 HB_HeuristicPosition(shaper_item);
623 const HB_ScriptEngine HB_ScriptEngines[] = {
633 { HB_HebrewShape, 0 },
635 { HB_ArabicShape, 0},
637 { HB_ArabicShape, 0},
639 { HB_BasicShape, 0 },
641 { HB_IndicShape, HB_IndicAttributes },
643 { HB_IndicShape, HB_IndicAttributes },
645 { HB_IndicShape, HB_IndicAttributes },
647 { HB_IndicShape, HB_IndicAttributes },
649 { HB_IndicShape, HB_IndicAttributes },
651 { HB_IndicShape, HB_IndicAttributes },
653 { HB_IndicShape, HB_IndicAttributes },
655 { HB_IndicShape, HB_IndicAttributes },
657 { HB_IndicShape, HB_IndicAttributes },
659 { HB_IndicShape, HB_IndicAttributes },
661 { HB_ThaiShape, HB_ThaiAttributes },
663 { HB_BasicShape, 0 },
665 { HB_TibetanShape, HB_TibetanAttributes },
667 { HB_MyanmarShape, HB_MyanmarAttributes },
669 { HB_BasicShape, 0 },
671 { HB_HangulShape, 0 },
673 { HB_BasicShape, 0 },
675 { HB_BasicShape, 0 },
677 { HB_KhmerShape, HB_KhmerAttributes },
682 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
683 const HB_ScriptItem *items, hb_uint32 numItems,
684 HB_CharAttributes *attributes)
686 memset(attributes, 0, stringLength * sizeof(HB_CharAttributes));
687 calcLineBreaks(string, stringLength, attributes);
689 for (hb_uint32 i = 0; i < numItems; ++i) {
690 HB_Script script = items[i].script;
691 if (script == HB_Script_Inherited)
692 script = HB_Script_Common;
693 HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
694 if (!attributeFunction)
696 attributeFunction(script, string, items[i].pos, items[i].length, attributes);
701 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
703 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
704 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
705 { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
706 { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
707 { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
708 { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
709 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
710 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
711 { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
712 { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
715 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
716 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
717 HB_CharAttributes *attributes)
719 if (stringLength == 0)
721 unsigned int brk = HB_GetWordClass(string[0]);
722 attributes[0].wordBoundary = true;
723 for (hb_uint32 i = 1; i < stringLength; ++i) {
724 if (!attributes[i].charStop) {
725 attributes[i].wordBoundary = false;
728 hb_uint32 nbrk = HB_GetWordClass(string[i]);
729 if (nbrk == HB_Word_Format) {
730 attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
733 BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
734 if (rule == Middle) {
736 hb_uint32 lookahead = i + 1;
737 while (lookahead < stringLength) {
738 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
739 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
743 if (testbrk == brk) {
745 while (i < lookahead)
746 attributes[i++].wordBoundary = false;
752 attributes[i].wordBoundary = (rule == Break);
758 enum SentenceBreakStates {
773 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
774 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
775 { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
776 { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
778 { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
779 { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
780 { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
781 { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
783 { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
784 { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
785 { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
786 { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
789 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
790 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
791 HB_CharAttributes *attributes)
793 if (stringLength == 0)
795 hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
796 attributes[0].sentenceBoundary = true;
797 for (hb_uint32 i = 1; i < stringLength; ++i) {
798 if (!attributes[i].charStop) {
799 attributes[i].sentenceBoundary = false;
802 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
803 if (brk == SB_Look) {
805 hb_uint32 lookahead = i + 1;
806 while (lookahead < stringLength) {
807 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
808 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
810 } else if (sbrk == HB_Sentence_Lower) {
816 if (brk == SB_Initial) {
817 while (i < lookahead)
818 attributes[i++].sentenceBoundary = false;
821 if (brk == SB_Break) {
822 attributes[i].sentenceBoundary = true;
823 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
825 attributes[i].sentenceBoundary = false;
831 static inline char *tag_to_string(HB_UInt tag)
833 static char string[5];
834 string[0] = (tag >> 24)&0xff;
835 string[1] = (tag >> 16)&0xff;
836 string[2] = (tag >> 8)&0xff;
837 string[3] = tag&0xff;
843 static void dump_string(HB_Buffer buffer)
845 for (uint i = 0; i < buffer->in_length; ++i) {
846 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
851 #define DEBUG if (1) ; else printf
854 #define DefaultLangSys 0xffff
855 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
866 static const OTScripts ot_scripts [] = {
868 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
870 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
872 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
874 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
876 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
878 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
880 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
882 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
884 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
886 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
888 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
890 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
892 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
894 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
896 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
898 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
900 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
902 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
904 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
906 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
908 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
910 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
912 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
914 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
916 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
918 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
920 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
922 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
924 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
926 static HB_Bool checkScript(HB_Face face, int script)
928 assert(script < HB_ScriptCount);
930 if (!face->gsub && !face->gpos)
933 unsigned int tag = ot_scripts[script].tag;
934 int requirements = ot_scripts[script].flags;
936 if (requirements & RequiresGsub) {
940 HB_UShort script_index;
941 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
943 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
944 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
950 if (requirements & RequiresGpos) {
954 HB_UShort script_index;
955 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
957 DEBUG("could not select script in gpos table: %d", error);
958 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
967 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
971 HB_Stream stream = 0;
976 error = tableFunc(font, tag, 0, &length);
979 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
982 stream->base = (HB_Byte*)malloc(length);
987 error = tableFunc(font, tag, stream->base, &length);
989 _hb_close_stream(stream);
992 stream->size = length;
994 stream->cursor = NULL;
998 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
1000 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
1004 face->isSymbolFont = false;
1008 face->current_script = HB_ScriptCount;
1009 face->current_flags = HB_ShaperFlag_Default;
1010 face->has_opentype_kerning = false;
1011 face->tmpAttributes = 0;
1012 face->tmpLogClusters = 0;
1013 face->glyphs_substituted = false;
1016 HB_Error error = HB_Err_Ok;
1018 HB_Stream gdefStream;
1020 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
1021 error = HB_Err_Not_Covered;
1022 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
1023 //DEBUG("error loading gdef table: %d", error);
1027 //DEBUG() << "trying to load gsub table";
1028 stream = getTableStream(font, tableFunc, TTAG_GSUB);
1029 error = HB_Err_Not_Covered;
1030 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
1032 if (error != HB_Err_Not_Covered) {
1033 //DEBUG("error loading gsub table: %d", error);
1035 //DEBUG("face doesn't have a gsub table");
1038 _hb_close_stream(stream);
1040 stream = getTableStream(font, tableFunc, TTAG_GPOS);
1041 error = HB_Err_Not_Covered;
1042 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1044 DEBUG("error loading gpos table: %d", error);
1046 _hb_close_stream(stream);
1048 _hb_close_stream(gdefStream);
1050 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1051 face->supported_scripts[i] = checkScript(face, i);
1053 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1061 void HB_FreeFace(HB_Face face)
1066 HB_Done_GPOS_Table(face->gpos);
1068 HB_Done_GSUB_Table(face->gsub);
1070 HB_Done_GDEF_Table(face->gdef);
1072 hb_buffer_free(face->buffer);
1073 if (face->tmpAttributes)
1074 free(face->tmpAttributes);
1075 if (face->tmpLogClusters)
1076 free(face->tmpLogClusters);
1080 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1082 HB_Script script = shaper_item->item.script;
1084 HB_Face face = shaper_item->face;
1085 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1086 return shaper_item->face->supported_scripts[script] ? true : false;
1088 face->current_script = script;
1089 face->current_flags = shaper_item->shaperFlags;
1091 if (!shaper_item->face->supported_scripts[script])
1094 assert(script < HB_ScriptCount);
1095 // find script in our list of supported scripts.
1096 unsigned int tag = ot_scripts[script].tag;
1098 if (face->gsub && features) {
1101 HB_FeatureList featurelist = face->gsub->FeatureList;
1102 int numfeatures = featurelist.FeatureCount;
1103 DEBUG("gsub table has %d features", numfeatures);
1104 for (int i = 0; i < numfeatures; i++) {
1105 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1106 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1110 HB_GSUB_Clear_Features(face->gsub);
1111 HB_UShort script_index;
1112 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1114 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1115 while (features->tag) {
1116 HB_UShort feature_index;
1117 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1119 DEBUG(" adding feature %s", tag_to_string(features->tag));
1120 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1128 face->has_opentype_kerning = false;
1131 HB_GPOS_Clear_Features(face->gpos);
1132 HB_UShort script_index;
1133 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1137 HB_FeatureList featurelist = face->gpos->FeatureList;
1138 int numfeatures = featurelist.FeatureCount;
1139 DEBUG("gpos table has %d features", numfeatures);
1140 for(int i = 0; i < numfeatures; i++) {
1141 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1142 HB_UShort feature_index;
1143 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1144 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1148 HB_UInt *feature_tag_list_buffer;
1149 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1151 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1152 while (*feature_tag_list) {
1153 HB_UShort feature_index;
1155 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1156 if (face->current_flags & HB_ShaperFlag_NoKerning)
1159 face->has_opentype_kerning = true;
1161 features = disabled_features;
1162 while (features->tag) {
1163 if (*feature_tag_list == features->tag) {
1169 // 'palt' should be turned off by default unless 'kern' is on
1170 if (!face->has_opentype_kerning &&
1171 *feature_tag_list == HB_MAKE_TAG('p', 'a', 'l', 't'))
1178 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1180 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1183 FREE(feature_tag_list_buffer);
1191 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1193 HB_GlyphAttributes *tmpAttributes;
1194 unsigned int *tmpLogClusters;
1196 HB_Face face = item->face;
1198 face->length = item->num_glyphs;
1200 hb_buffer_clear(face->buffer);
1202 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1205 face->tmpAttributes = tmpAttributes;
1207 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1208 if (!tmpLogClusters)
1210 face->tmpLogClusters = tmpLogClusters;
1212 for (int i = 0; i < face->length; ++i) {
1213 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1214 face->tmpAttributes[i] = item->attributes[i];
1215 face->tmpLogClusters[i] = item->log_clusters[i];
1219 DEBUG("-----------------------------------------");
1220 // DEBUG("log clusters before shaping:");
1221 // for (int j = 0; j < length; j++)
1222 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1223 DEBUG("original glyphs: %p", item->glyphs);
1224 for (int i = 0; i < length; ++i)
1225 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1226 // dump_string(hb_buffer);
1229 face->glyphs_substituted = false;
1231 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1232 if (error && error != HB_Err_Not_Covered)
1234 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1238 // DEBUG("log clusters before shaping:");
1239 // for (int j = 0; j < length; j++)
1240 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1241 DEBUG("shaped glyphs:");
1242 for (int i = 0; i < length; ++i)
1243 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1244 DEBUG("-----------------------------------------");
1245 // dump_string(hb_buffer);
1251 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1253 HB_Face face = item->face;
1255 bool glyphs_positioned = false;
1257 if (face->buffer->positions)
1258 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1259 // #### check that passing "false,false" is correct
1260 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1263 if (!face->glyphs_substituted && !glyphs_positioned) {
1264 HB_HeuristicPosition(item);
1265 return true; // nothing to do for us
1268 // make sure we have enough space to write everything back
1269 if (availableGlyphs < (int)face->buffer->in_length) {
1270 item->num_glyphs = face->buffer->in_length;
1274 HB_Glyph *glyphs = item->glyphs;
1275 HB_GlyphAttributes *attributes = item->attributes;
1277 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1278 glyphs[i] = face->buffer->in_string[i].gindex;
1279 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
1280 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
1281 attributes[i].clusterStart = false;
1283 item->num_glyphs = face->buffer->in_length;
1285 if (doLogClusters && face->glyphs_substituted) {
1286 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1287 unsigned short *logClusters = item->log_clusters;
1288 int clusterStart = 0;
1290 // #### the reconstruction of the logclusters currently does not work if the original string
1291 // contains surrogate pairs
1292 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1293 int ci = face->buffer->in_string[i].cluster;
1294 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1295 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1296 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1297 for (int j = oldCi; j < ci; j++)
1298 logClusters[j] = clusterStart;
1303 for (int j = oldCi; j < face->length; j++)
1304 logClusters[j] = clusterStart;
1307 // calulate the advances for the shaped glyphs
1308 // DEBUG("unpositioned: ");
1310 // positioning code:
1311 if (glyphs_positioned) {
1312 HB_GetGlyphAdvances(item);
1313 HB_Position positions = face->buffer->positions;
1314 HB_Fixed *advances = item->advances;
1316 // DEBUG("positioned glyphs:");
1317 for (unsigned int i = 0; i < face->buffer->in_length; i++) {
1318 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1319 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1320 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1321 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1322 // positions[i].back, positions[i].new_advance);
1324 HB_Fixed adjustment = positions[i].x_advance;
1326 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1327 adjustment = HB_FIXED_ROUND(adjustment);
1329 if (positions[i].new_advance) {
1330 advances[i] = adjustment;
1332 advances[i] += adjustment;
1336 HB_FixedPoint *offsets = item->offsets;
1337 offsets[i].x = positions[i].x_pos;
1338 offsets[i].y = positions[i].y_pos;
1339 while (positions[i - back].back) {
1340 back += positions[i - back].back;
1341 offsets[i].x += positions[i - back].x_pos;
1342 offsets[i].y += positions[i - back].y_pos;
1344 offsets[i].y = -offsets[i].y;
1346 if (item->item.bidiLevel % 2) {
1347 // ### may need to go back multiple glyphs like in ltr
1348 back = positions[i].back;
1350 offsets[i].x -= advances[i-back];
1353 while (positions[i - back].back) {
1354 back += positions[i - back].back;
1355 offsets[i].x -= advances[i-back];
1358 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1359 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1361 item->kerning_applied = face->has_opentype_kerning;
1363 HB_HeuristicPosition(item);
1367 if (doLogClusters) {
1368 DEBUG("log clusters after shaping:\n");
1369 for (unsigned int j = 0; j < item->item.length; j++)
1370 DEBUG(" log[%d] = %d\n", j, item->log_clusters[j]);
1372 DEBUG("final glyphs:\n");
1373 for (unsigned int i = 0; i < item->num_glyphs; ++i)
1374 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d offset=%d/%d\n",
1375 glyphs[i], face->buffer->in_string[i].cluster, attributes[i].mark,
1376 attributes[i].combiningClass, attributes[i].clusterStart,
1377 item->advances[i] >> 6,
1378 item->offsets[i].x >> 6, item->offsets[i].y >> 6);
1379 DEBUG("-----------------------------------------\n");
1384 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1386 HB_Bool result = false;
1387 if (shaper_item->num_glyphs < shaper_item->item.length) {
1388 shaper_item->num_glyphs = shaper_item->item.length;
1391 assert(shaper_item->item.script < HB_ScriptCount);
1392 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1393 shaper_item->glyphIndicesPresent = false;