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_AllocFace(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;
1015 face->font_for_init = font;
1016 face->get_font_table_func = tableFunc;
1021 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
1023 HB_Face face = HB_AllocFace(font, tableFunc);
1025 face = HB_LoadFace(face);
1029 HB_Face HB_LoadFace(HB_Face face)
1031 void *font = face->font_for_init;
1035 HB_GetFontTableFunc tableFunc = face->get_font_table_func;
1037 face->get_font_table_func = 0;
1038 face->font_for_init = 0;
1040 HB_Error error = HB_Err_Ok;
1042 HB_Stream gdefStream;
1044 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
1045 error = HB_Err_Not_Covered;
1046 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
1047 //DEBUG("error loading gdef table: %d", error);
1051 //DEBUG() << "trying to load gsub table";
1052 stream = getTableStream(font, tableFunc, TTAG_GSUB);
1053 error = HB_Err_Not_Covered;
1054 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
1056 if (error != HB_Err_Not_Covered) {
1057 //DEBUG("error loading gsub table: %d", error);
1059 //DEBUG("face doesn't have a gsub table");
1062 _hb_close_stream(stream);
1064 stream = getTableStream(font, tableFunc, TTAG_GPOS);
1065 error = HB_Err_Not_Covered;
1066 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1068 DEBUG("error loading gpos table: %d", error);
1070 _hb_close_stream(stream);
1072 _hb_close_stream(gdefStream);
1074 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1075 face->supported_scripts[i] = checkScript(face, i);
1077 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1085 void HB_FreeFace(HB_Face face)
1090 HB_Done_GPOS_Table(face->gpos);
1092 HB_Done_GSUB_Table(face->gsub);
1094 HB_Done_GDEF_Table(face->gdef);
1096 hb_buffer_free(face->buffer);
1097 if (face->tmpAttributes)
1098 free(face->tmpAttributes);
1099 if (face->tmpLogClusters)
1100 free(face->tmpLogClusters);
1104 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1106 HB_Script script = shaper_item->item.script;
1108 HB_Face face = shaper_item->face;
1109 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1110 return shaper_item->face->supported_scripts[script] ? true : false;
1112 face->current_script = script;
1113 face->current_flags = shaper_item->shaperFlags;
1115 if (!shaper_item->face->supported_scripts[script])
1118 assert(script < HB_ScriptCount);
1119 // find script in our list of supported scripts.
1120 unsigned int tag = ot_scripts[script].tag;
1122 if (face->gsub && features) {
1125 HB_FeatureList featurelist = face->gsub->FeatureList;
1126 int numfeatures = featurelist.FeatureCount;
1127 DEBUG("gsub table has %d features", numfeatures);
1128 for (int i = 0; i < numfeatures; i++) {
1129 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1130 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1134 HB_GSUB_Clear_Features(face->gsub);
1135 HB_UShort script_index;
1136 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1138 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1139 while (features->tag) {
1140 HB_UShort feature_index;
1141 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1143 DEBUG(" adding feature %s", tag_to_string(features->tag));
1144 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1152 face->has_opentype_kerning = false;
1155 HB_GPOS_Clear_Features(face->gpos);
1156 HB_UShort script_index;
1157 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1161 HB_FeatureList featurelist = face->gpos->FeatureList;
1162 int numfeatures = featurelist.FeatureCount;
1163 DEBUG("gpos table has %d features", numfeatures);
1164 for(int i = 0; i < numfeatures; i++) {
1165 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1166 HB_UShort feature_index;
1167 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1168 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1172 HB_UInt *feature_tag_list_buffer;
1173 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1175 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1176 while (*feature_tag_list) {
1177 HB_UShort feature_index;
1179 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1180 if (face->current_flags & HB_ShaperFlag_NoKerning)
1183 face->has_opentype_kerning = true;
1185 features = disabled_features;
1186 while (features->tag) {
1187 if (*feature_tag_list == features->tag) {
1193 // 'palt' should be turned off by default unless 'kern' is on
1194 if (!face->has_opentype_kerning &&
1195 *feature_tag_list == HB_MAKE_TAG('p', 'a', 'l', 't'))
1202 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1204 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1207 FREE(feature_tag_list_buffer);
1215 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1217 HB_GlyphAttributes *tmpAttributes;
1218 unsigned int *tmpLogClusters;
1220 HB_Face face = item->face;
1222 face->length = item->num_glyphs;
1224 hb_buffer_clear(face->buffer);
1226 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1229 face->tmpAttributes = tmpAttributes;
1231 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1232 if (!tmpLogClusters)
1234 face->tmpLogClusters = tmpLogClusters;
1236 for (int i = 0; i < face->length; ++i) {
1237 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1238 face->tmpAttributes[i] = item->attributes[i];
1239 face->tmpLogClusters[i] = item->log_clusters[i];
1243 DEBUG("-----------------------------------------");
1244 // DEBUG("log clusters before shaping:");
1245 // for (int j = 0; j < length; j++)
1246 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1247 DEBUG("original glyphs: %p", item->glyphs);
1248 for (int i = 0; i < length; ++i)
1249 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1250 // dump_string(hb_buffer);
1253 face->glyphs_substituted = false;
1255 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1256 if (error && error != HB_Err_Not_Covered)
1258 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1262 // DEBUG("log clusters before shaping:");
1263 // for (int j = 0; j < length; j++)
1264 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1265 DEBUG("shaped glyphs:");
1266 for (int i = 0; i < length; ++i)
1267 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1268 DEBUG("-----------------------------------------");
1269 // dump_string(hb_buffer);
1275 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1277 HB_Face face = item->face;
1279 bool glyphs_positioned = false;
1281 if (face->buffer->positions)
1282 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1283 // #### check that passing "false,false" is correct
1284 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1287 if (!face->glyphs_substituted && !glyphs_positioned) {
1288 HB_HeuristicPosition(item);
1289 return true; // nothing to do for us
1292 // make sure we have enough space to write everything back
1293 if (availableGlyphs < (int)face->buffer->in_length) {
1294 item->num_glyphs = face->buffer->in_length;
1298 HB_Glyph *glyphs = item->glyphs;
1299 HB_GlyphAttributes *attributes = item->attributes;
1301 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1302 glyphs[i] = face->buffer->in_string[i].gindex;
1303 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
1304 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
1305 attributes[i].clusterStart = false;
1307 item->num_glyphs = face->buffer->in_length;
1309 if (doLogClusters && face->glyphs_substituted) {
1310 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1311 unsigned short *logClusters = item->log_clusters;
1312 int clusterStart = 0;
1314 // #### the reconstruction of the logclusters currently does not work if the original string
1315 // contains surrogate pairs
1316 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1317 int ci = face->buffer->in_string[i].cluster;
1318 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1319 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1320 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1321 for (int j = oldCi; j < ci; j++)
1322 logClusters[j] = clusterStart;
1327 for (int j = oldCi; j < face->length; j++)
1328 logClusters[j] = clusterStart;
1331 // calulate the advances for the shaped glyphs
1332 // DEBUG("unpositioned: ");
1334 // positioning code:
1335 if (glyphs_positioned) {
1336 HB_GetGlyphAdvances(item);
1337 HB_Position positions = face->buffer->positions;
1338 HB_Fixed *advances = item->advances;
1340 // DEBUG("positioned glyphs:");
1341 for (unsigned int i = 0; i < face->buffer->in_length; i++) {
1342 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1343 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1344 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1345 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1346 // positions[i].back, positions[i].new_advance);
1348 HB_Fixed adjustment = positions[i].x_advance;
1350 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1351 adjustment = HB_FIXED_ROUND(adjustment);
1353 if (positions[i].new_advance) {
1354 advances[i] = adjustment;
1356 advances[i] += adjustment;
1360 HB_FixedPoint *offsets = item->offsets;
1361 offsets[i].x = positions[i].x_pos;
1362 offsets[i].y = positions[i].y_pos;
1363 while (positions[i - back].back) {
1364 back += positions[i - back].back;
1365 offsets[i].x += positions[i - back].x_pos;
1366 offsets[i].y += positions[i - back].y_pos;
1368 offsets[i].y = -offsets[i].y;
1370 if (item->item.bidiLevel % 2) {
1371 // ### may need to go back multiple glyphs like in ltr
1372 back = positions[i].back;
1374 offsets[i].x -= advances[i-back];
1377 while (positions[i - back].back) {
1378 back += positions[i - back].back;
1379 offsets[i].x -= advances[i-back];
1382 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1383 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1385 item->kerning_applied = face->has_opentype_kerning;
1387 HB_HeuristicPosition(item);
1391 if (doLogClusters) {
1392 DEBUG("log clusters after shaping:\n");
1393 for (unsigned int j = 0; j < item->item.length; j++)
1394 DEBUG(" log[%d] = %d\n", j, item->log_clusters[j]);
1396 DEBUG("final glyphs:\n");
1397 for (unsigned int i = 0; i < item->num_glyphs; ++i)
1398 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d offset=%d/%d\n",
1399 glyphs[i], face->buffer->in_string[i].cluster, attributes[i].mark,
1400 attributes[i].combiningClass, attributes[i].clusterStart,
1401 item->advances[i] >> 6,
1402 item->offsets[i].x >> 6, item->offsets[i].y >> 6);
1403 DEBUG("-----------------------------------------\n");
1408 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1410 HB_Bool result = false;
1411 if (shaper_item->num_glyphs < shaper_item->item.length) {
1412 shaper_item->num_glyphs = shaper_item->item.length;
1415 assert(shaper_item->item.script < HB_ScriptCount);
1416 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1417 shaper_item->glyphIndicesPresent = false;