Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / third_party / harfbuzz / src / harfbuzz-shaper.cpp
blob5ca8078cad4ba32bceb12b9d52b1f8145710afc6
1 /*
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
16 * DAMAGE.
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"
29 #include <assert.h>
30 #include <stdio.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:
45 EX->AL from DB to IB
46 SY->AL from DB to IB
47 SY->PO from DB to IB
48 SY->PR from DB to IB
49 SY->OP from DB to IB
50 AL->PR from DB to IB
51 AL->PO from DB to IB
52 PR->PR from DB to IB
53 PO->PO from DB to IB
54 PR->PO from DB to IB
55 PO->PR from DB to IB
56 HY->PO from DB to IB
57 HY->PR from DB to IB
58 HY->OP from DB to IB
59 NU->EX from PB to IB
60 EX->PO from DB to IB
63 // The following line break classes are not treated by the table:
64 // AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
66 enum break_class {
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 }
110 #undef DB
111 #undef IB
112 #undef CI
113 #undef CP
114 #undef PB
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)
133 if (!len)
134 return;
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;
147 int lcls = cls;
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];
157 // handle surrogates
158 if (ncls == HB_LineBreak_SG) {
159 if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) {
160 continue;
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;
165 } else {
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)
184 goto next;
187 int tcls = ncls;
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];
197 switch (brk) {
198 case DirectBreak:
199 lineBreakType = HB_Break;
200 if (uc[i-1] == 0xad) // soft hyphen
201 lineBreakType = HB_SoftHyphen;
202 break;
203 case IndirectBreak:
204 lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
205 break;
206 case CombiningIndirectBreak:
207 lineBreakType = HB_NoBreak;
208 if (lcls == HB_LineBreak_SP){
209 if (i > 1)
210 charAttributes[i-2].lineBreakType = HB_Break;
211 } else {
212 goto next_no_cls_update;
214 break;
215 case CombiningProhibitedBreak:
216 lineBreakType = HB_NoBreak;
217 if (lcls != HB_LineBreak_SP)
218 goto next_no_cls_update;
219 case ProhibitedBreak:
220 default:
221 break;
224 next:
225 cls = ncls;
226 next_no_cls_update:
227 lcls = ncls;
228 grapheme = ngrapheme;
229 charAttributes[i-1].lineBreakType = lineBreakType;
231 charAttributes[len-1].lineBreakType = HB_ForcedBreak;
234 // --------------------------------------------------------------------------------------------------------------------------------------------
236 // Basic processing
238 // --------------------------------------------------------------------------------------------------------------------------------------------
240 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
242 int nmarks = glast - gfrom;
243 assert(nmarks > 0);
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);
263 else
264 offsetBase += size;
265 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
266 // qDebug("offset = %f", offsetBase);
268 bool rightToLeft = item->item.bidiLevel % 2;
270 int i;
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);
279 HB_FixedPoint p;
280 p.x = p.y = 0;
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.
288 if (cmb < 200) {
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)
296 offset +=1;
297 // below
298 if ((cmb >= 10 && cmb <= 18) ||
299 cmb == 20 || cmb == 22 ||
300 cmb == 29 || cmb == 32)
301 cmb = HB_Combining_Below;
302 // above
303 else if (cmb == 23 || cmb == 27 || cmb == 28 ||
304 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
305 cmb = HB_Combining_Above;
306 //below-right
307 else if (cmb == 9 || cmb == 103 || cmb == 118)
308 cmb = HB_Combining_BelowRight;
309 // above-right
310 else if (cmb == 24 || cmb == 107 || cmb == 122)
311 cmb = HB_Combining_AboveRight;
312 else if (cmb == 25)
313 cmb = HB_Combining_AboveLeft;
314 // fixed:
315 // 19 21
319 // combining marks of different class don't interact. Reset the rectangle.
320 if (cmb != lastCmb) {
321 //qDebug("resetting rect");
322 attachmentRect = baseMetrics;
325 switch(cmb) {
326 case HB_Combining_DoubleBelow:
327 // ### wrong in rtl context!
328 case HB_Combining_BelowLeft:
329 p.y += offset;
330 case HB_Combining_BelowLeftAttached:
331 p.x += attachmentRect.x - markMetrics.x;
332 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
333 break;
334 case HB_Combining_Below:
335 p.y += offset;
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;
341 break;
342 case HB_Combining_BelowRight:
343 p.y += offset;
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;
347 break;
348 case HB_Combining_Left:
349 p.x -= offset;
350 case HB_Combining_LeftAttached:
351 break;
352 case HB_Combining_Right:
353 p.x += offset;
354 case HB_Combining_RightAttached:
355 break;
356 case HB_Combining_DoubleAbove:
357 // ### wrong in RTL context!
358 case HB_Combining_AboveLeft:
359 p.y -= offset;
360 case HB_Combining_AboveLeftAttached:
361 p.x += attachmentRect.x - markMetrics.x;
362 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
363 break;
364 case HB_Combining_Above:
365 p.y -= offset;
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;
371 break;
372 case HB_Combining_AboveRight:
373 p.y -= offset;
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;
377 break;
379 case HB_Combining_IotaSubscript:
380 default:
381 break;
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;
394 lastCmb = cmb;
395 if (rightToLeft) {
396 item->offsets[gfrom+i].x = p.x;
397 item->offsets[gfrom+i].y = p.y;
398 } else {
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;
411 int cEnd = -1;
412 int i = item->num_glyphs;
413 while (i--) {
414 if (cEnd == -1 && attributes[i].mark) {
415 cEnd = i;
416 } else if (cEnd != -1 && !attributes[i].mark) {
417 positionCluster(item, i, cEnd);
418 cEnd = -1;
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 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
434 HB_GlyphAttributes *attributes = item->attributes;
435 unsigned short *logClusters = item->log_clusters;
437 hb_uint32 glyph_pos = 0;
438 hb_uint32 i;
439 for (i = 0; i < length; i++) {
440 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
441 && HB_IsLowSurrogate(uc[i + 1])) {
442 logClusters[i] = glyph_pos;
443 logClusters[++i] = glyph_pos;
444 } else {
445 logClusters[i] = glyph_pos;
447 ++glyph_pos;
450 // first char in a run is never (treated as) a mark
451 int cStart = 0;
452 const bool symbolFont = item->face->isSymbolFont;
453 attributes[0].mark = false;
454 attributes[0].clusterStart = true;
455 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
457 int pos = 0;
458 HB_CharCategory lastCat;
459 int dummy;
460 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
461 for (i = 1; i < length; ++i) {
462 if (logClusters[i] == pos)
463 // same glyph
464 continue;
465 ++pos;
466 while (pos < logClusters[i]) {
467 attributes[pos] = attributes[pos-1];
468 ++pos;
470 // hide soft-hyphens by default
471 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
472 attributes[pos].dontPrint = true;
473 HB_CharCategory cat;
474 int cmb;
475 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
476 if (cat != HB_Mark_NonSpacing) {
477 attributes[pos].mark = false;
478 attributes[pos].clusterStart = true;
479 attributes[pos].combiningClass = 0;
480 cStart = logClusters[i];
481 } else {
482 if (cmb == 0) {
483 // Fix 0 combining classes
484 if ((uc[pos] & 0xff00) == 0x0e00) {
485 // thai or lao
486 if (uc[pos] == 0xe31 ||
487 uc[pos] == 0xe34 ||
488 uc[pos] == 0xe35 ||
489 uc[pos] == 0xe36 ||
490 uc[pos] == 0xe37 ||
491 uc[pos] == 0xe47 ||
492 uc[pos] == 0xe4c ||
493 uc[pos] == 0xe4d ||
494 uc[pos] == 0xe4e) {
495 cmb = HB_Combining_AboveRight;
496 } else if (uc[pos] == 0xeb1 ||
497 uc[pos] == 0xeb4 ||
498 uc[pos] == 0xeb5 ||
499 uc[pos] == 0xeb6 ||
500 uc[pos] == 0xeb7 ||
501 uc[pos] == 0xebb ||
502 uc[pos] == 0xecc ||
503 uc[pos] == 0xecd) {
504 cmb = HB_Combining_Above;
505 } else if (uc[pos] == 0xebc) {
506 cmb = HB_Combining_Below;
511 attributes[pos].mark = true;
512 attributes[pos].clusterStart = false;
513 attributes[pos].combiningClass = cmb;
514 logClusters[i] = cStart;
516 // one gets an inter character justification point if the current char is not a non spacing mark.
517 // as then the current char belongs to the last one and one gets a space justification point
518 // after the space char.
519 if (lastCat == HB_Separator_Space)
520 attributes[pos-1].justification = HB_Space;
521 else if (cat != HB_Mark_NonSpacing)
522 attributes[pos-1].justification = HB_Character;
523 else
524 attributes[pos-1].justification = HB_NoJustification;
526 lastCat = cat;
528 pos = logClusters[length-1];
529 if (lastCat == HB_Separator_Space)
530 attributes[pos].justification = HB_Space;
531 else
532 attributes[pos].justification = HB_Character;
535 #ifndef NO_OPENTYPE
536 static const HB_OpenTypeFeature basic_features[] = {
537 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
538 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
539 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
540 {0, 0}
542 #endif
544 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
546 if (shaper_item->glyphIndicesPresent) {
547 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
548 shaper_item->glyphIndicesPresent = false;
549 return true;
551 return shaper_item->font->klass
552 ->convertStringToGlyphIndices(shaper_item->font,
553 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
554 shaper_item->glyphs, &shaper_item->num_glyphs,
555 shaper_item->item.bidiLevel % 2);
558 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
560 #ifndef NO_OPENTYPE
561 const int availableGlyphs = shaper_item->num_glyphs;
562 #endif
564 if (!HB_ConvertStringToGlyphIndices(shaper_item))
565 return false;
567 HB_HeuristicSetGlyphAttributes(shaper_item);
569 #ifndef NO_OPENTYPE
570 if (HB_SelectScript(shaper_item, basic_features)) {
571 HB_OpenTypeShape(shaper_item, /*properties*/0);
572 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
574 #endif
576 HB_HeuristicPosition(shaper_item);
577 return true;
580 const HB_ScriptEngine HB_ScriptEngines[] = {
581 // Common
582 { HB_BasicShape, 0},
583 // Greek
584 { HB_GreekShape, 0},
585 // Cyrillic
586 { HB_BasicShape, 0},
587 // Armenian
588 { HB_BasicShape, 0},
589 // Hebrew
590 { HB_HebrewShape, 0 },
591 // Arabic
592 { HB_ArabicShape, 0},
593 // Syriac
594 { HB_ArabicShape, 0},
595 // Thaana
596 { HB_BasicShape, 0 },
597 // Devanagari
598 { HB_IndicShape, HB_IndicAttributes },
599 // Bengali
600 { HB_IndicShape, HB_IndicAttributes },
601 // Gurmukhi
602 { HB_IndicShape, HB_IndicAttributes },
603 // Gujarati
604 { HB_IndicShape, HB_IndicAttributes },
605 // Oriya
606 { HB_IndicShape, HB_IndicAttributes },
607 // Tamil
608 { HB_IndicShape, HB_IndicAttributes },
609 // Telugu
610 { HB_IndicShape, HB_IndicAttributes },
611 // Kannada
612 { HB_IndicShape, HB_IndicAttributes },
613 // Malayalam
614 { HB_IndicShape, HB_IndicAttributes },
615 // Sinhala
616 { HB_IndicShape, HB_IndicAttributes },
617 // Thai
618 { HB_BasicShape, HB_ThaiAttributes },
619 // Lao
620 { HB_BasicShape, 0 },
621 // Tibetan
622 { HB_TibetanShape, HB_TibetanAttributes },
623 // Myanmar
624 { HB_MyanmarShape, HB_MyanmarAttributes },
625 // Georgian
626 { HB_BasicShape, 0 },
627 // Hangul
628 { HB_HangulShape, 0 },
629 // Ogham
630 { HB_BasicShape, 0 },
631 // Runic
632 { HB_BasicShape, 0 },
633 // Khmer
634 { HB_KhmerShape, HB_KhmerAttributes },
635 // N'Ko
636 { HB_ArabicShape, 0}
639 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
640 const HB_ScriptItem *items, hb_uint32 numItems,
641 HB_CharAttributes *attributes)
643 calcLineBreaks(string, stringLength, attributes);
645 for (hb_uint32 i = 0; i < numItems; ++i) {
646 HB_Script script = items[i].script;
647 if (script == HB_Script_Inherited)
648 script = HB_Script_Common;
649 HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
650 if (!attributeFunction)
651 continue;
652 attributeFunction(script, string, items[i].pos, items[i].length, attributes);
657 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
659 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
660 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
661 { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
662 { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
663 { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
664 { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
665 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
666 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
667 { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
668 { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
671 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
672 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
673 HB_CharAttributes *attributes)
675 if (stringLength == 0)
676 return;
677 unsigned int brk = HB_GetWordClass(string[0]);
678 attributes[0].wordBoundary = true;
679 for (hb_uint32 i = 1; i < stringLength; ++i) {
680 if (!attributes[i].charStop) {
681 attributes[i].wordBoundary = false;
682 continue;
684 hb_uint32 nbrk = HB_GetWordClass(string[i]);
685 if (nbrk == HB_Word_Format) {
686 attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
687 continue;
689 BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
690 if (rule == Middle) {
691 rule = Break;
692 hb_uint32 lookahead = i + 1;
693 while (lookahead < stringLength) {
694 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
695 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
696 ++lookahead;
697 continue;
699 if (testbrk == brk) {
700 rule = NoBreak;
701 while (i < lookahead)
702 attributes[i++].wordBoundary = false;
703 nbrk = testbrk;
705 break;
708 attributes[i].wordBoundary = (rule == Break);
709 brk = nbrk;
714 enum SentenceBreakStates {
715 SB_Initial,
716 SB_Upper,
717 SB_UpATerm,
718 SB_ATerm,
719 SB_ATermC,
720 SB_ACS,
721 SB_STerm,
722 SB_STermC,
723 SB_SCS,
724 SB_BAfter,
725 SB_Break,
726 SB_Look
729 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
730 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
731 { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
732 { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
734 { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
735 { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
736 { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
737 { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
739 { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
740 { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
741 { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
742 { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
745 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
746 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
747 HB_CharAttributes *attributes)
749 if (stringLength == 0)
750 return;
751 hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
752 attributes[0].sentenceBoundary = true;
753 for (hb_uint32 i = 1; i < stringLength; ++i) {
754 if (!attributes[i].charStop) {
755 attributes[i].sentenceBoundary = false;
756 continue;
758 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
759 if (brk == SB_Look) {
760 brk = SB_Break;
761 hb_uint32 lookahead = i + 1;
762 while (lookahead < stringLength) {
763 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
764 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
765 break;
766 } else if (sbrk == HB_Sentence_Lower) {
767 brk = SB_Initial;
768 break;
770 ++lookahead;
772 if (brk == SB_Initial) {
773 while (i < lookahead)
774 attributes[i++].sentenceBoundary = false;
777 if (brk == SB_Break) {
778 attributes[i].sentenceBoundary = true;
779 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
780 } else {
781 attributes[i].sentenceBoundary = false;
787 static inline char *tag_to_string(HB_UInt tag)
789 static char string[5];
790 string[0] = (tag >> 24)&0xff;
791 string[1] = (tag >> 16)&0xff;
792 string[2] = (tag >> 8)&0xff;
793 string[3] = tag&0xff;
794 string[4] = 0;
795 return string;
798 #ifdef OT_DEBUG
799 static void dump_string(HB_Buffer buffer)
801 for (uint i = 0; i < buffer->in_length; ++i) {
802 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
805 #define DEBUG printf
806 #else
807 #define DEBUG if (1) ; else printf
808 #endif
810 #define DefaultLangSys 0xffff
811 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
813 enum {
814 RequiresGsub = 1,
815 RequiresGpos = 2
818 struct OTScripts {
819 unsigned int tag;
820 int flags;
822 static const OTScripts ot_scripts [] = {
823 // Common
824 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
825 // Greek
826 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
827 // Cyrillic
828 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
829 // Armenian
830 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
831 // Hebrew
832 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
833 // Arabic
834 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
835 // Syriac
836 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
837 // Thaana
838 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
839 // Devanagari
840 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
841 // Bengali
842 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
843 // Gurmukhi
844 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
845 // Gujarati
846 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
847 // Oriya
848 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
849 // Tamil
850 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
851 // Telugu
852 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
853 // Kannada
854 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
855 // Malayalam
856 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
857 // Sinhala
858 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
859 // Thai
860 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
861 // Lao
862 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
863 // Tibetan
864 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
865 // Myanmar
866 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
867 // Georgian
868 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
869 // Hangul
870 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
871 // Ogham
872 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
873 // Runic
874 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
875 // Khmer
876 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
877 // N'Ko
878 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
880 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
882 static HB_Bool checkScript(HB_Face face, int script)
884 assert(script < HB_ScriptCount);
886 if (!face->gsub && !face->gpos)
887 return false;
889 unsigned int tag = ot_scripts[script].tag;
890 int requirements = ot_scripts[script].flags;
892 if (requirements & RequiresGsub) {
893 if (!face->gsub)
894 return false;
896 HB_UShort script_index;
897 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
898 if (error) {
899 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
900 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
901 if (error)
902 return false;
906 if (requirements & RequiresGpos) {
907 if (!face->gpos)
908 return false;
910 HB_UShort script_index;
911 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
912 if (error) {
913 DEBUG("could not select script in gpos table: %d", error);
914 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
915 if (error)
916 return false;
920 return true;
923 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
925 HB_Error error;
926 HB_UInt length = 0;
927 HB_Stream stream = 0;
929 if (!font)
930 return 0;
932 error = tableFunc(font, tag, 0, &length);
933 if (error)
934 return 0;
935 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
936 if (!stream)
937 return 0;
938 stream->base = (HB_Byte*)malloc(length);
939 if (!stream->base) {
940 free(stream);
941 return 0;
943 error = tableFunc(font, tag, stream->base, &length);
944 if (error) {
945 _hb_close_stream(stream);
946 return 0;
948 stream->size = length;
949 stream->pos = 0;
950 stream->cursor = NULL;
951 return stream;
954 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
956 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
957 if (!face)
958 return 0;
960 face->isSymbolFont = false;
961 face->gdef = 0;
962 face->gpos = 0;
963 face->gsub = 0;
964 face->current_script = HB_ScriptCount;
965 face->current_flags = HB_ShaperFlag_Default;
966 face->has_opentype_kerning = false;
967 face->tmpAttributes = 0;
968 face->tmpLogClusters = 0;
969 face->glyphs_substituted = false;
970 face->buffer = 0;
972 HB_Error error = HB_Err_Ok;
973 HB_Stream stream;
974 HB_Stream gdefStream;
976 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
977 error = HB_Err_Not_Covered;
978 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
979 //DEBUG("error loading gdef table: %d", error);
980 face->gdef = 0;
983 //DEBUG() << "trying to load gsub table";
984 stream = getTableStream(font, tableFunc, TTAG_GSUB);
985 error = HB_Err_Not_Covered;
986 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
987 face->gsub = 0;
988 if (error != HB_Err_Not_Covered) {
989 //DEBUG("error loading gsub table: %d", error);
990 } else {
991 //DEBUG("face doesn't have a gsub table");
994 _hb_close_stream(stream);
996 stream = getTableStream(font, tableFunc, TTAG_GPOS);
997 error = HB_Err_Not_Covered;
998 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
999 face->gpos = 0;
1000 DEBUG("error loading gpos table: %d", error);
1002 _hb_close_stream(stream);
1004 _hb_close_stream(gdefStream);
1006 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1007 face->supported_scripts[i] = checkScript(face, i);
1009 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1010 HB_FreeFace(face);
1011 return 0;
1014 return face;
1017 void HB_FreeFace(HB_Face face)
1019 if (!face)
1020 return;
1021 if (face->gpos)
1022 HB_Done_GPOS_Table(face->gpos);
1023 if (face->gsub)
1024 HB_Done_GSUB_Table(face->gsub);
1025 if (face->gdef)
1026 HB_Done_GDEF_Table(face->gdef);
1027 if (face->buffer)
1028 hb_buffer_free(face->buffer);
1029 if (face->tmpAttributes)
1030 free(face->tmpAttributes);
1031 if (face->tmpLogClusters)
1032 free(face->tmpLogClusters);
1033 free(face);
1036 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1038 HB_Script script = shaper_item->item.script;
1040 if (!shaper_item->face->supported_scripts[script])
1041 return false;
1043 HB_Face face = shaper_item->face;
1044 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1045 return true;
1047 face->current_script = script;
1048 face->current_flags = shaper_item->shaperFlags;
1050 assert(script < HB_ScriptCount);
1051 // find script in our list of supported scripts.
1052 unsigned int tag = ot_scripts[script].tag;
1054 if (face->gsub && features) {
1055 #ifdef OT_DEBUG
1057 HB_FeatureList featurelist = face->gsub->FeatureList;
1058 int numfeatures = featurelist.FeatureCount;
1059 DEBUG("gsub table has %d features", numfeatures);
1060 for (int i = 0; i < numfeatures; i++) {
1061 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1062 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1065 #endif
1066 HB_GSUB_Clear_Features(face->gsub);
1067 HB_UShort script_index;
1068 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1069 if (!error) {
1070 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1071 while (features->tag) {
1072 HB_UShort feature_index;
1073 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1074 if (!error) {
1075 DEBUG(" adding feature %s", tag_to_string(features->tag));
1076 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1078 ++features;
1083 // reset
1084 face->has_opentype_kerning = false;
1086 if (face->gpos) {
1087 HB_GPOS_Clear_Features(face->gpos);
1088 HB_UShort script_index;
1089 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1090 if (!error) {
1091 #ifdef OT_DEBUG
1093 HB_FeatureList featurelist = face->gpos->FeatureList;
1094 int numfeatures = featurelist.FeatureCount;
1095 DEBUG("gpos table has %d features", numfeatures);
1096 for(int i = 0; i < numfeatures; i++) {
1097 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1098 HB_UShort feature_index;
1099 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1100 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1103 #endif
1104 HB_UInt *feature_tag_list_buffer;
1105 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1106 if (!error) {
1107 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1108 while (*feature_tag_list) {
1109 HB_UShort feature_index;
1110 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1111 if (face->current_flags & HB_ShaperFlag_NoKerning) {
1112 ++feature_tag_list;
1113 continue;
1115 face->has_opentype_kerning = true;
1117 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1118 if (!error)
1119 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1120 ++feature_tag_list;
1122 FREE(feature_tag_list_buffer);
1127 return true;
1130 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1132 HB_GlyphAttributes *tmpAttributes;
1133 unsigned int *tmpLogClusters;
1135 HB_Face face = item->face;
1137 face->length = item->num_glyphs;
1139 hb_buffer_clear(face->buffer);
1141 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1142 if (!tmpAttributes)
1143 return false;
1144 face->tmpAttributes = tmpAttributes;
1146 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1147 if (!tmpLogClusters)
1148 return false;
1149 face->tmpLogClusters = tmpLogClusters;
1151 const int itemLength = item->item.length;
1152 assert(itemLength > 0);
1153 for (int i = 0; i < face->length; ++i) {
1154 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1155 face->tmpAttributes[i] = item->attributes[i];
1156 face->tmpLogClusters[i] = i < itemLength ? item->log_clusters[i] : item->log_clusters[itemLength - 1];
1159 #ifdef OT_DEBUG
1160 DEBUG("-----------------------------------------");
1161 // DEBUG("log clusters before shaping:");
1162 // for (int j = 0; j < length; j++)
1163 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1164 DEBUG("original glyphs: %p", item->glyphs);
1165 for (int i = 0; i < length; ++i)
1166 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1167 // dump_string(hb_buffer);
1168 #endif
1170 face->glyphs_substituted = false;
1171 if (face->gsub) {
1172 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1173 if (error && error != HB_Err_Not_Covered)
1174 return false;
1175 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1178 #ifdef OT_DEBUG
1179 // DEBUG("log clusters before shaping:");
1180 // for (int j = 0; j < length; j++)
1181 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1182 DEBUG("shaped glyphs:");
1183 for (int i = 0; i < length; ++i)
1184 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1185 DEBUG("-----------------------------------------");
1186 // dump_string(hb_buffer);
1187 #endif
1189 return true;
1192 /* See comments near the definition of HB_ShaperFlag_ForceMarksToZeroWidth for a description
1193 of why this function exists. */
1194 void HB_FixupZeroWidth(HB_ShaperItem *item)
1196 HB_UShort property;
1198 if (!item->face->gdef)
1199 return;
1201 for (unsigned int i = 0; i < item->num_glyphs; ++i) {
1202 /* If the glyph is a mark, force its advance to zero. */
1203 if (HB_GDEF_Get_Glyph_Property (item->face->gdef, item->glyphs[i], &property) == HB_Err_Ok &&
1204 property == HB_GDEF_MARK) {
1205 item->advances[i] = 0;
1210 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1212 HB_Face face = item->face;
1214 bool glyphs_positioned = false;
1215 if (face->gpos) {
1216 if (face->buffer->positions)
1217 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1218 // #### check that passing "false,false" is correct
1219 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1222 if (!face->glyphs_substituted && !glyphs_positioned) {
1223 HB_GetGlyphAdvances(item);
1224 if (item->face->current_flags & HB_ShaperFlag_ForceMarksToZeroWidth)
1225 HB_FixupZeroWidth(item);
1226 return true; // nothing to do for us
1229 // make sure we have enough space to write everything back
1230 if (availableGlyphs < (int)face->buffer->in_length) {
1231 item->num_glyphs = face->buffer->in_length;
1232 return false;
1235 HB_Glyph *glyphs = item->glyphs;
1236 HB_GlyphAttributes *attributes = item->attributes;
1238 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1239 glyphs[i] = face->buffer->in_string[i].gindex;
1240 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
1241 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
1242 attributes[i].clusterStart = false;
1244 item->num_glyphs = face->buffer->in_length;
1246 if (doLogClusters && face->glyphs_substituted) {
1247 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1248 unsigned short *logClusters = item->log_clusters;
1249 int clusterStart = 0;
1250 int oldCi = 0;
1251 // #### the reconstruction of the logclusters currently does not work if the original string
1252 // contains surrogate pairs
1253 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1254 int ci = face->buffer->in_string[i].cluster;
1255 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1256 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1257 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1258 for (int j = oldCi; j < ci; j++)
1259 logClusters[j] = clusterStart;
1260 clusterStart = i;
1261 oldCi = ci;
1264 for (int j = oldCi; j < face->length; j++)
1265 logClusters[j] = clusterStart;
1268 // calulate the advances for the shaped glyphs
1269 // DEBUG("unpositioned: ");
1271 // positioning code:
1272 if (glyphs_positioned) {
1273 HB_GetGlyphAdvances(item);
1274 HB_Position positions = face->buffer->positions;
1275 HB_Fixed *advances = item->advances;
1277 // DEBUG("positioned glyphs:");
1278 for (unsigned int i = 0; i < face->buffer->in_length; i++) {
1279 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1280 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1281 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1282 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1283 // positions[i].back, positions[i].new_advance);
1285 HB_Fixed adjustment = positions[i].x_advance;
1287 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1288 adjustment = HB_FIXED_ROUND(adjustment);
1290 if (positions[i].new_advance == 0)
1291 advances[i] += adjustment;
1293 int back = 0;
1294 HB_FixedPoint *offsets = item->offsets;
1295 offsets[i].x = positions[i].x_pos;
1296 offsets[i].y = positions[i].y_pos;
1297 while (positions[i - back].back) {
1298 back += positions[i - back].back;
1299 offsets[i].x += positions[i - back].x_pos;
1300 offsets[i].y += positions[i - back].y_pos;
1302 offsets[i].y = -offsets[i].y;
1304 if (item->item.bidiLevel % 2) {
1305 // ### may need to go back multiple glyphs like in ltr
1306 back = positions[i].back;
1307 while (back--)
1308 offsets[i].x -= advances[i-back];
1309 } else {
1310 back = 0;
1311 while (positions[i - back].back) {
1312 back += positions[i - back].back;
1313 offsets[i].x -= advances[i-back];
1316 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1317 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1319 item->kerning_applied = face->has_opentype_kerning;
1320 } else {
1321 HB_HeuristicPosition(item);
1324 #ifdef OT_DEBUG
1325 if (doLogClusters) {
1326 DEBUG("log clusters after shaping:");
1327 for (int j = 0; j < length; j++)
1328 DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1330 DEBUG("final glyphs:");
1331 for (int i = 0; i < (int)hb_buffer->in_length; ++i)
1332 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
1333 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
1334 glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
1335 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1336 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1337 DEBUG("-----------------------------------------");
1338 #endif
1339 return true;
1342 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1344 HB_Bool result = false;
1345 if (shaper_item->num_glyphs < shaper_item->item.length) {
1346 shaper_item->num_glyphs = shaper_item->item.length;
1347 return false;
1349 assert(shaper_item->item.script < HB_ScriptCount);
1350 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1351 shaper_item->glyphIndicesPresent = false;
1352 return result;