1 /*
2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This is part of HarfBuzz, an OpenType Layout engine library.
5 *
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.
11 *
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.
17 *
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.
23 */
24
25 #include "harfbuzz-shaper.h"
26 #include "harfbuzz-shaper-private.h"
27 extern "C" {
28 #include "harfbuzz-debug.h"
29 }
30 #include "harfbuzz-stream-private.h"
31 #include <assert.h>
32 #include <stdio.h>
33
34 #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
35 #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
36
37 // -----------------------------------------------------------------------------------------------------
38 //
39 // The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
40 //
41 // -----------------------------------------------------------------------------------------------------
42
43 /* The Unicode algorithm does in our opinion allow line breaks at some
44 places they shouldn't be allowed. The following changes were thus
45 made in comparison to the Unicode reference:
46
47 EX->AL from DB to IB
48 SY->AL from DB to IB
49 SY->PO from DB to IB
50 SY->PR from DB to IB
51 SY->OP from DB to IB
52 AL->PR from DB to IB
53 AL->PO from DB to IB
54 PR->PR from DB to IB
55 PO->PO from DB to IB
56 PR->PO from DB to IB
57 PO->PR from DB to IB
58 HY->PO from DB to IB
59 HY->PR from DB to IB
60 HY->OP from DB to IB
61 NU->EX from PB to IB
62 EX->PO from DB to IB
63 */
64
65 // The following line break classes are not treated by the table:
66 // AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
67
68 enum break_class {
69 // the first 4 values have to agree with the enum in QCharAttributes
70 ProhibitedBreak, // PB in table
71 DirectBreak, // DB in table
72 IndirectBreak, // IB in table
73 CombiningIndirectBreak, // CI in table
74 CombiningProhibitedBreak // CP in table
75 };
76 #define DB DirectBreak
77 #define IB IndirectBreak
78 #define CI CombiningIndirectBreak
79 #define CP CombiningProhibitedBreak
80 #define PB ProhibitedBreak
81
82 static const hb_uint8 breakTable[HB_LineBreak_JT+1][HB_LineBreak_JT+1] =
83 {
84 /* 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 */
85 /* 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 },
86 /* 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 },
87 /* 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 },
88 /* 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 },
89 /* 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 },
90 /* 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 },
91 /* 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 },
92 /* 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 },
93 /* 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 },
94 /* 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 },
95 /* 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 },
96 /* 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 },
97 /* 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 },
98 /* 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 },
99 /* 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 },
100 /* 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 },
101 /* 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 },
102 /* 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 },
103 /* 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 },
104 /* 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 },
105 /* 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 },
106 /* 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 },
107 /* 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 },
108 /* 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 },
109 /* 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 },
110 /* 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 }
111 };
112 #undef DB
113 #undef IB
114 #undef CI
115 #undef CP
116 #undef PB
117
118 static const hb_uint8 graphemeTable[HB_Grapheme_LVT + 1][HB_Grapheme_LVT + 1] =
119 {
120 // Other, CR, LF, Control,Extend,L, V, T, LV, LVT
121 { true , true , true , true , true , true , true , true , true , true }, // Other,
122 { true , true , true , true , true , true , true , true , true , true }, // CR,
123 { true , false, true , true , true , true , true , true , true , true }, // LF,
124 { true , true , true , true , true , true , true , true , true , true }, // Control,
125 { false, true , true , true , false, false, false, false, false, false }, // Extend,
126 { true , true , true , true , true , false, true , true , true , true }, // L,
127 { true , true , true , true , true , false, false, true , false, true }, // V,
128 { true , true , true , true , true , true , false, false, false, false }, // T,
129 { true , true , true , true , true , false, true , true , true , true }, // LV,
130 { true , true , true , true , true , false, true , true , true , true }, // LVT
131 };
132
calcLineBreaks(const HB_UChar16 * uc,hb_uint32 len,HB_CharAttributes * charAttributes)133 static void calcLineBreaks(const HB_UChar16 *uc, hb_uint32 len, HB_CharAttributes *charAttributes)
134 {
135 if (!len)
136 return;
137
138 // ##### can this fail if the first char is a surrogate?
139 HB_LineBreakClass cls;
140 HB_GraphemeClass grapheme;
141 HB_GetGraphemeAndLineBreakClass(*uc, &grapheme, &cls);
142 // handle case where input starts with an LF
143 if (cls == HB_LineBreak_LF)
144 cls = HB_LineBreak_BK;
145
146 charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK);
147 charAttributes[0].charStop = true;
148
149 int lcls = cls;
150 for (hb_uint32 i = 1; i < len; ++i) {
151 charAttributes[i].whiteSpace = false;
152 charAttributes[i].charStop = true;
153
154 HB_UChar32 code = uc[i];
155 HB_GraphemeClass ngrapheme;
156 HB_LineBreakClass ncls;
157 HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
158 charAttributes[i].charStop = graphemeTable[ngrapheme][grapheme];
159 // handle surrogates
160 if (ncls == HB_LineBreak_SG) {
161 if (HB_IsHighSurrogate(uc[i]) && i < len - 1 && HB_IsLowSurrogate(uc[i+1])) {
162 continue;
163 } else if (HB_IsLowSurrogate(uc[i]) && HB_IsHighSurrogate(uc[i-1])) {
164 code = HB_SurrogateToUcs4(uc[i-1], uc[i]);
165 HB_GetGraphemeAndLineBreakClass(code, &ngrapheme, &ncls);
166 charAttributes[i].charStop = false;
167 } else {
168 ncls = HB_LineBreak_AL;
169 }
170 }
171
172 // set white space and char stop flag
173 if (ncls >= HB_LineBreak_SP)
174 charAttributes[i].whiteSpace = true;
175
176 HB_LineBreakType lineBreakType = HB_NoBreak;
177 if (cls >= HB_LineBreak_LF) {
178 lineBreakType = HB_ForcedBreak;
179 } else if(cls == HB_LineBreak_CR) {
180 lineBreakType = (ncls == HB_LineBreak_LF) ? HB_NoBreak : HB_ForcedBreak;
181 }
182
183 if (ncls == HB_LineBreak_SP)
184 goto next_no_cls_update;
185 if (ncls >= HB_LineBreak_CR)
186 goto next;
187
188 {
189 int tcls = ncls;
190 // for south east asian chars that require a complex (dictionary analysis), the unicode
191 // standard recommends to treat them as AL. thai_attributes and other attribute methods that
192 // do dictionary analysis can override
193 if (tcls >= HB_LineBreak_SA)
194 tcls = HB_LineBreak_AL;
195 if (cls >= HB_LineBreak_SA)
196 cls = HB_LineBreak_AL;
197
198 int brk = breakTable[cls][tcls];
199 switch (brk) {
200 case DirectBreak:
201 lineBreakType = HB_Break;
202 if (uc[i-1] == 0xad) // soft hyphen
203 lineBreakType = HB_SoftHyphen;
204 break;
205 case IndirectBreak:
206 lineBreakType = (lcls == HB_LineBreak_SP) ? HB_Break : HB_NoBreak;
207 break;
208 case CombiningIndirectBreak:
209 lineBreakType = HB_NoBreak;
210 if (lcls == HB_LineBreak_SP){
211 if (i > 1)
212 charAttributes[i-2].lineBreakType = HB_Break;
213 } else {
214 goto next_no_cls_update;
215 }
216 break;
217 case CombiningProhibitedBreak:
218 lineBreakType = HB_NoBreak;
219 if (lcls != HB_LineBreak_SP)
220 goto next_no_cls_update;
221 case ProhibitedBreak:
222 default:
223 break;
224 }
225 }
226 next:
227 cls = ncls;
228 next_no_cls_update:
229 lcls = ncls;
230 grapheme = ngrapheme;
231 charAttributes[i-1].lineBreakType = lineBreakType;
232 }
233 charAttributes[len-1].lineBreakType = HB_ForcedBreak;
234 }
235
236 // --------------------------------------------------------------------------------------------------------------------------------------------
237 //
238 // Basic processing
239 //
240 // --------------------------------------------------------------------------------------------------------------------------------------------
241
positionCluster(HB_ShaperItem * item,int gfrom,int glast)242 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
243 {
244 int nmarks = glast - gfrom;
245 assert(nmarks > 0);
246
247 HB_Glyph *glyphs = item->glyphs;
248 HB_GlyphAttributes *attributes = item->attributes;
249
250 HB_GlyphMetrics baseMetrics;
251 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
252
253 if (item->item.script == HB_Script_Hebrew
254 && (-baseMetrics.y) > baseMetrics.height)
255 // we need to attach below the baseline, because of the hebrew iud.
256 baseMetrics.height = -baseMetrics.y;
257
258 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast);
259 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff);
260
261 HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10;
262 HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4;
263 if (size > HB_FIXED_CONSTANT(4))
264 offsetBase += HB_FIXED_CONSTANT(4);
265 else
266 offsetBase += size;
267 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1;
268 // qDebug("offset = %f", offsetBase);
269
270 bool rightToLeft = item->item.bidiLevel % 2;
271
272 int i;
273 unsigned char lastCmb = 0;
274 HB_GlyphMetrics attachmentRect;
275 memset(&attachmentRect, 0, sizeof(attachmentRect));
276
277 for(i = 1; i <= nmarks; i++) {
278 HB_Glyph mark = glyphs[gfrom+i];
279 HB_GlyphMetrics markMetrics;
280 item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics);
281 HB_FixedPoint p;
282 p.x = p.y = 0;
283 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff);
284
285 HB_Fixed offset = offsetBase;
286 unsigned char cmb = attributes[gfrom+i].combiningClass;
287
288 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some
289 // bits in the glyphAttributes structure.
290 if (cmb < 200) {
291 // fixed position classes. We approximate by mapping to one of the others.
292 // currently I added only the ones for arabic, hebrew, lao and thai.
293
294 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
295
296 // add a bit more offset to arabic, a bit hacky
297 if (cmb >= 27 && cmb <= 36 && offset < 3)
298 offset +=1;
299 // below
300 if ((cmb >= 10 && cmb <= 18) ||
301 cmb == 20 || cmb == 22 ||
302 cmb == 29 || cmb == 32)
303 cmb = HB_Combining_Below;
304 // above
305 else if (cmb == 23 || cmb == 27 || cmb == 28 ||
306 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36))
307 cmb = HB_Combining_Above;
308 //below-right
309 else if (cmb == 9 || cmb == 103 || cmb == 118)
310 cmb = HB_Combining_BelowRight;
311 // above-right
312 else if (cmb == 24 || cmb == 107 || cmb == 122)
313 cmb = HB_Combining_AboveRight;
314 else if (cmb == 25)
315 cmb = HB_Combining_AboveLeft;
316 // fixed:
317 // 19 21
318
319 }
320
321 // combining marks of different class don't interact. Reset the rectangle.
322 if (cmb != lastCmb) {
323 //qDebug("resetting rect");
324 attachmentRect = baseMetrics;
325 }
326
327 switch(cmb) {
328 case HB_Combining_DoubleBelow:
329 // ### wrong in rtl context!
330 case HB_Combining_BelowLeft:
331 p.y += offset;
332 case HB_Combining_BelowLeftAttached:
333 p.x += attachmentRect.x - markMetrics.x;
334 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
335 break;
336 case HB_Combining_Below:
337 p.y += offset;
338 case HB_Combining_BelowAttached:
339 p.x += attachmentRect.x - markMetrics.x;
340 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y;
341
342 p.x += (attachmentRect.width - markMetrics.width) / 2;
343 break;
344 case HB_Combining_BelowRight:
345 p.y += offset;
346 case HB_Combining_BelowRightAttached:
347 p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x;
348 p.y += attachmentRect.y + attachmentRect.height - markMetrics.y;
349 break;
350 case HB_Combining_Left:
351 p.x -= offset;
352 case HB_Combining_LeftAttached:
353 break;
354 case HB_Combining_Right:
355 p.x += offset;
356 case HB_Combining_RightAttached:
357 break;
358 case HB_Combining_DoubleAbove:
359 // ### wrong in RTL context!
360 case HB_Combining_AboveLeft:
361 p.y -= offset;
362 case HB_Combining_AboveLeftAttached:
363 p.x += attachmentRect.x - markMetrics.x;
364 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
365 break;
366 case HB_Combining_Above:
367 p.y -= offset;
368 case HB_Combining_AboveAttached:
369 p.x += attachmentRect.x - markMetrics.x;
370 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
371
372 p.x += (attachmentRect.width - markMetrics.width) / 2;
373 break;
374 case HB_Combining_AboveRight:
375 p.y -= offset;
376 case HB_Combining_AboveRightAttached:
377 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width;
378 p.y += attachmentRect.y - markMetrics.y - markMetrics.height;
379 break;
380
381 case HB_Combining_IotaSubscript:
382 default:
383 break;
384 }
385 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y());
386 markMetrics.x += p.x;
387 markMetrics.y += p.y;
388
389 HB_GlyphMetrics unitedAttachmentRect = attachmentRect;
390 unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x);
391 unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y);
392 unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x;
393 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y;
394 attachmentRect = unitedAttachmentRect;
395
396 lastCmb = cmb;
397 if (rightToLeft) {
398 item->offsets[gfrom+i].x = p.x;
399 item->offsets[gfrom+i].y = p.y;
400 } else {
401 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset;
402 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset;
403 }
404 item->advances[gfrom+i] = 0;
405 }
406 }
407
HB_HeuristicPosition(HB_ShaperItem * item)408 void HB_HeuristicPosition(HB_ShaperItem *item)
409 {
410 HB_GetGlyphAdvances(item);
411 HB_GlyphAttributes *attributes = item->attributes;
412
413 int cEnd = -1;
414 int i = item->num_glyphs;
415 while (i--) {
416 if (cEnd == -1 && attributes[i].mark) {
417 cEnd = i;
418 } else if (cEnd != -1 && !attributes[i].mark) {
419 positionCluster(item, i, cEnd);
420 cEnd = -1;
421 }
422 }
423 }
424
425 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs
426 // and no reordering.
427 // also computes logClusters heuristically
HB_HeuristicSetGlyphAttributes(HB_ShaperItem * item)428 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
429 {
430 const HB_UChar16 *uc = item->string + item->item.pos;
431 hb_uint32 length = item->item.length;
432
433 // ### zeroWidth and justification are missing here!!!!!
434
435 // BEGIN android-changed
436 // We apply the same fix for Chrome to Android.
437 // Chrome team will talk with upsteam about it.
438 assert(length <= item->num_glyphs);
439 // END android-changed
440
441 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
442 HB_GlyphAttributes *attributes = item->attributes;
443 unsigned short *logClusters = item->log_clusters;
444
445 hb_uint32 glyph_pos = 0;
446 hb_uint32 i;
447 for (i = 0; i < length; i++) {
448 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
449 && HB_IsLowSurrogate(uc[i + 1])) {
450 logClusters[i] = glyph_pos;
451 logClusters[++i] = glyph_pos;
452 } else {
453 logClusters[i] = glyph_pos;
454 }
455 ++glyph_pos;
456 }
457
458 // BEGIN android-removed
459 // We apply the same fix for Chrome to Android.
460 // Chrome team will talk with upsteam about it
461 //
462 // assert(glyph_pos == item->num_glyphs);
463 //
464 // END android-removed
465
466 // first char in a run is never (treated as) a mark
467 int cStart = 0;
468 const bool symbolFont = item->face->isSymbolFont;
469 attributes[0].mark = false;
470 attributes[0].clusterStart = true;
471 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
472
473 int pos = 0;
474 HB_CharCategory lastCat;
475 int dummy;
476 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
477 for (i = 1; i < length; ++i) {
478 if (logClusters[i] == pos)
479 // same glyph
480 continue;
481 ++pos;
482 while (pos < logClusters[i]) {
483 attributes[pos] = attributes[pos-1];
484 ++pos;
485 }
486 // hide soft-hyphens by default
487 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
488 attributes[pos].dontPrint = true;
489 HB_CharCategory cat;
490 int cmb;
491 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
492 if (cat != HB_Mark_NonSpacing) {
493 attributes[pos].mark = false;
494 attributes[pos].clusterStart = true;
495 attributes[pos].combiningClass = 0;
496 cStart = logClusters[i];
497 } else {
498 if (cmb == 0) {
499 // Fix 0 combining classes
500 if ((uc[pos] & 0xff00) == 0x0e00) {
501 // thai or lao
502 if (uc[pos] == 0xe31 ||
503 uc[pos] == 0xe34 ||
504 uc[pos] == 0xe35 ||
505 uc[pos] == 0xe36 ||
506 uc[pos] == 0xe37 ||
507 uc[pos] == 0xe47 ||
508 uc[pos] == 0xe4c ||
509 uc[pos] == 0xe4d ||
510 uc[pos] == 0xe4e) {
511 cmb = HB_Combining_AboveRight;
512 } else if (uc[pos] == 0xeb1 ||
513 uc[pos] == 0xeb4 ||
514 uc[pos] == 0xeb5 ||
515 uc[pos] == 0xeb6 ||
516 uc[pos] == 0xeb7 ||
517 uc[pos] == 0xebb ||
518 uc[pos] == 0xecc ||
519 uc[pos] == 0xecd) {
520 cmb = HB_Combining_Above;
521 } else if (uc[pos] == 0xebc) {
522 cmb = HB_Combining_Below;
523 }
524 }
525 }
526
527 attributes[pos].mark = true;
528 attributes[pos].clusterStart = false;
529 attributes[pos].combiningClass = cmb;
530 logClusters[i] = cStart;
531 }
532 // one gets an inter character justification point if the current char is not a non spacing mark.
533 // as then the current char belongs to the last one and one gets a space justification point
534 // after the space char.
535 if (lastCat == HB_Separator_Space)
536 attributes[pos-1].justification = HB_Space;
537 else if (cat != HB_Mark_NonSpacing)
538 attributes[pos-1].justification = HB_Character;
539 else
540 attributes[pos-1].justification = HB_NoJustification;
541
542 lastCat = cat;
543 }
544 pos = logClusters[length-1];
545 if (lastCat == HB_Separator_Space)
546 attributes[pos].justification = HB_Space;
547 else
548 attributes[pos].justification = HB_Character;
549 }
550
551 #ifndef NO_OPENTYPE
552 static const HB_OpenTypeFeature basic_features[] = {
553 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
554 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
555 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
556 {0, 0}
557 };
558 #endif
559
HB_ConvertStringToGlyphIndices(HB_ShaperItem * shaper_item)560 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
561 {
562 if (shaper_item->glyphIndicesPresent) {
563 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
564 shaper_item->glyphIndicesPresent = false;
565 return true;
566 }
567 return shaper_item->font->klass
568 ->convertStringToGlyphIndices(shaper_item->font,
569 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
570 shaper_item->glyphs, &shaper_item->num_glyphs,
571 shaper_item->item.bidiLevel % 2);
572 }
573
HB_BasicShape(HB_ShaperItem * shaper_item)574 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
575 {
576 #ifndef NO_OPENTYPE
577 const int availableGlyphs = shaper_item->num_glyphs;
578 #endif
579
580 if (!HB_ConvertStringToGlyphIndices(shaper_item))
581 return false;
582
583 HB_HeuristicSetGlyphAttributes(shaper_item);
584
585 #ifndef NO_OPENTYPE
586 if (HB_SelectScript(shaper_item, basic_features)) {
587 HB_OpenTypeShape(shaper_item, /*properties*/0);
588 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
589 }
590 #endif
591
592 HB_HeuristicPosition(shaper_item);
593 return true;
594 }
595
596 const HB_ScriptEngine HB_ScriptEngines[] = {
597 // Common
598 { HB_BasicShape, 0},
599 // Greek
600 { HB_GreekShape, 0},
601 // Cyrillic
602 { HB_BasicShape, 0},
603 // Armenian
604 { HB_BasicShape, 0},
605 // Hebrew
606 { HB_HebrewShape, 0 },
607 // Arabic
608 { HB_ArabicShape, 0},
609 // Syriac
610 { HB_ArabicShape, 0},
611 // Thaana
612 { HB_BasicShape, 0 },
613 // Devanagari
614 { HB_IndicShape, HB_IndicAttributes },
615 // Bengali
616 { HB_IndicShape, HB_IndicAttributes },
617 // Gurmukhi
618 { HB_IndicShape, HB_IndicAttributes },
619 // Gujarati
620 { HB_IndicShape, HB_IndicAttributes },
621 // Oriya
622 { HB_IndicShape, HB_IndicAttributes },
623 // Tamil
624 { HB_IndicShape, HB_IndicAttributes },
625 // Telugu
626 { HB_IndicShape, HB_IndicAttributes },
627 // Kannada
628 { HB_IndicShape, HB_IndicAttributes },
629 // Malayalam
630 { HB_IndicShape, HB_IndicAttributes },
631 // Sinhala
632 { HB_IndicShape, HB_IndicAttributes },
633 // Thai
634 { HB_BasicShape, HB_ThaiAttributes },
635 // Lao
636 { HB_BasicShape, 0 },
637 // Tibetan
638 { HB_TibetanShape, HB_TibetanAttributes },
639 // Myanmar
640 { HB_MyanmarShape, HB_MyanmarAttributes },
641 // Georgian
642 { HB_BasicShape, 0 },
643 // Hangul
644 { HB_HangulShape, 0 },
645 // Ogham
646 { HB_BasicShape, 0 },
647 // Runic
648 { HB_BasicShape, 0 },
649 // Khmer
650 { HB_KhmerShape, HB_KhmerAttributes },
651 // N'Ko
652 { HB_ArabicShape, 0}
653 };
654
HB_GetCharAttributes(const HB_UChar16 * string,hb_uint32 stringLength,const HB_ScriptItem * items,hb_uint32 numItems,HB_CharAttributes * attributes)655 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
656 const HB_ScriptItem *items, hb_uint32 numItems,
657 HB_CharAttributes *attributes)
658 {
659 calcLineBreaks(string, stringLength, attributes);
660
661 for (hb_uint32 i = 0; i < numItems; ++i) {
662 HB_Script script = items[i].script;
663 if (script == HB_Script_Inherited)
664 script = HB_Script_Common;
665 HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
666 if (!attributeFunction)
667 continue;
668 attributeFunction(script, string, items[i].pos, items[i].length, attributes);
669 }
670 }
671
672
673 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
674
675 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
676 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
677 { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
678 { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
679 { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
680 { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
681 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
682 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
683 { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
684 { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
685 };
686
HB_GetWordBoundaries(const HB_UChar16 * string,hb_uint32 stringLength,const HB_ScriptItem *,hb_uint32,HB_CharAttributes * attributes)687 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
688 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
689 HB_CharAttributes *attributes)
690 {
691 if (stringLength == 0)
692 return;
693 unsigned int brk = HB_GetWordClass(string[0]);
694 attributes[0].wordBoundary = true;
695 for (hb_uint32 i = 1; i < stringLength; ++i) {
696 if (!attributes[i].charStop) {
697 attributes[i].wordBoundary = false;
698 continue;
699 }
700 hb_uint32 nbrk = HB_GetWordClass(string[i]);
701 if (nbrk == HB_Word_Format) {
702 attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
703 continue;
704 }
705 BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
706 if (rule == Middle) {
707 rule = Break;
708 hb_uint32 lookahead = i + 1;
709 while (lookahead < stringLength) {
710 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
711 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
712 ++lookahead;
713 continue;
714 }
715 if (testbrk == brk) {
716 rule = NoBreak;
717 while (i < lookahead)
718 attributes[i++].wordBoundary = false;
719 nbrk = testbrk;
720 }
721 break;
722 }
723 }
724 attributes[i].wordBoundary = (rule == Break);
725 brk = nbrk;
726 }
727 }
728
729
730 enum SentenceBreakStates {
731 SB_Initial,
732 SB_Upper,
733 SB_UpATerm,
734 SB_ATerm,
735 SB_ATermC,
736 SB_ACS,
737 SB_STerm,
738 SB_STermC,
739 SB_SCS,
740 SB_BAfter,
741 SB_Break,
742 SB_Look
743 };
744
745 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
746 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
747 { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
748 { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
749
750 { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
751 { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
752 { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
753 { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
754
755 { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
756 { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
757 { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
758 { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
759 };
760
HB_GetSentenceBoundaries(const HB_UChar16 * string,hb_uint32 stringLength,const HB_ScriptItem *,hb_uint32,HB_CharAttributes * attributes)761 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
762 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
763 HB_CharAttributes *attributes)
764 {
765 if (stringLength == 0)
766 return;
767 hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
768 attributes[0].sentenceBoundary = true;
769 for (hb_uint32 i = 1; i < stringLength; ++i) {
770 if (!attributes[i].charStop) {
771 attributes[i].sentenceBoundary = false;
772 continue;
773 }
774 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
775 if (brk == SB_Look) {
776 brk = SB_Break;
777 hb_uint32 lookahead = i + 1;
778 while (lookahead < stringLength) {
779 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
780 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
781 break;
782 } else if (sbrk == HB_Sentence_Lower) {
783 brk = SB_Initial;
784 break;
785 }
786 ++lookahead;
787 }
788 if (brk == SB_Initial) {
789 while (i < lookahead)
790 attributes[i++].sentenceBoundary = false;
791 }
792 }
793 if (brk == SB_Break) {
794 attributes[i].sentenceBoundary = true;
795 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
796 } else {
797 attributes[i].sentenceBoundary = false;
798 }
799 }
800 }
801
802
tag_to_string(HB_UInt tag)803 static inline char *tag_to_string(HB_UInt tag)
804 {
805 static char string[5];
806 string[0] = (tag >> 24)&0xff;
807 string[1] = (tag >> 16)&0xff;
808 string[2] = (tag >> 8)&0xff;
809 string[3] = tag&0xff;
810 string[4] = 0;
811 return string;
812 }
813
814 #ifdef OT_DEBUG
dump_string(HB_Buffer buffer)815 static void dump_string(HB_Buffer buffer)
816 {
817 for (uint i = 0; i < buffer->in_length; ++i) {
818 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
819 }
820 }
821 #define DEBUG printf
822 #else
823 #define DEBUG HBDebug
824 #endif
825
826 #define DefaultLangSys 0xffff
827 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
828
829 enum {
830 RequiresGsub = 1,
831 RequiresGpos = 2
832 };
833
834 struct OTScripts {
835 unsigned int tag;
836 int flags;
837 };
838 static const OTScripts ot_scripts [] = {
839 // Common
840 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
841 // Greek
842 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
843 // Cyrillic
844 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
845 // Armenian
846 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
847 // Hebrew
848 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
849 // Arabic
850 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
851 // Syriac
852 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
853 // Thaana
854 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
855 // Devanagari
856 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
857 // Bengali
858 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
859 // Gurmukhi
860 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
861 // Gujarati
862 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
863 // Oriya
864 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
865 // Tamil
866 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
867 // Telugu
868 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
869 // Kannada
870 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
871 // Malayalam
872 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
873 // Sinhala
874 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
875 // Thai
876 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
877 // Lao
878 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
879 // Tibetan
880 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
881 // Myanmar
882 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
883 // Georgian
884 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
885 // Hangul
886 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
887 // Ogham
888 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
889 // Runic
890 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
891 // Khmer
892 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
893 // N'Ko
894 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
895 };
896 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
897
checkScript(HB_Face face,int script)898 static HB_Bool checkScript(HB_Face face, int script)
899 {
900 assert(script < HB_ScriptCount);
901
902 if (!face->gsub && !face->gpos)
903 return false;
904
905 unsigned int tag = ot_scripts[script].tag;
906 int requirements = ot_scripts[script].flags;
907
908 if (requirements & RequiresGsub) {
909 if (!face->gsub)
910 return false;
911
912 HB_UShort script_index;
913 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
914 if (error) {
915 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
916 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
917 if (error)
918 return false;
919 }
920 }
921
922 if (requirements & RequiresGpos) {
923 if (!face->gpos)
924 return false;
925
926 HB_UShort script_index;
927 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
928 if (error) {
929 DEBUG("could not select script in gpos table: %d", error);
930 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
931 if (error)
932 return false;
933 }
934
935 }
936 return true;
937 }
938
getTableStream(void * font,HB_GetFontTableFunc tableFunc,HB_Tag tag)939 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
940 {
941 HB_Error error;
942 HB_UInt length = 0;
943 HB_Stream stream = 0;
944
945 if (!font)
946 return 0;
947
948 error = tableFunc(font, tag, 0, &length);
949 if (error)
950 return 0;
951 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
952 if (!stream)
953 return 0;
954 stream->base = (HB_Byte*)malloc(length);
955 if (!stream->base) {
956 free(stream);
957 return 0;
958 }
959 error = tableFunc(font, tag, stream->base, &length);
960 if (error) {
961 _hb_close_stream(stream);
962 return 0;
963 }
964 stream->size = length;
965 stream->pos = 0;
966 stream->cursor = NULL;
967 return stream;
968 }
969
HB_NewFace(void * font,HB_GetFontTableFunc tableFunc)970 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
971 {
972 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
973 if (!face)
974 return 0;
975
976 face->isSymbolFont = false;
977 face->gdef = 0;
978 face->gpos = 0;
979 face->gsub = 0;
980 face->current_script = HB_ScriptCount;
981 face->current_flags = HB_ShaperFlag_Default;
982 face->has_opentype_kerning = false;
983 face->tmpAttributes = 0;
984 face->tmpLogClusters = 0;
985 face->glyphs_substituted = false;
986 face->buffer = 0;
987
988 HB_Error error = HB_Err_Ok;
989 HB_Stream stream;
990 HB_Stream gdefStream;
991
992 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
993 error = HB_Err_Not_Covered;
994 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
995 //DEBUG("error loading gdef table: %d", error);
996 face->gdef = 0;
997 }
998
999 //DEBUG() << "trying to load gsub table";
1000 stream = getTableStream(font, tableFunc, TTAG_GSUB);
1001 error = HB_Err_Not_Covered;
1002 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
1003 face->gsub = 0;
1004 if (error != HB_Err_Not_Covered) {
1005 //DEBUG("error loading gsub table: %d", error);
1006 } else {
1007 //DEBUG("face doesn't have a gsub table");
1008 }
1009 }
1010 _hb_close_stream(stream);
1011
1012 stream = getTableStream(font, tableFunc, TTAG_GPOS);
1013 error = HB_Err_Not_Covered;
1014 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1015 face->gpos = 0;
1016 DEBUG("error loading gpos table: %d", error);
1017 }
1018 _hb_close_stream(stream);
1019
1020 _hb_close_stream(gdefStream);
1021
1022 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1023 face->supported_scripts[i] = checkScript(face, i);
1024
1025 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1026 HB_FreeFace(face);
1027 return 0;
1028 }
1029
1030 return face;
1031 }
1032
HB_FreeFace(HB_Face face)1033 void HB_FreeFace(HB_Face face)
1034 {
1035 if (!face)
1036 return;
1037 if (face->gpos)
1038 HB_Done_GPOS_Table(face->gpos);
1039 if (face->gsub)
1040 HB_Done_GSUB_Table(face->gsub);
1041 if (face->gdef)
1042 HB_Done_GDEF_Table(face->gdef);
1043 if (face->buffer)
1044 hb_buffer_free(face->buffer);
1045 if (face->tmpAttributes)
1046 free(face->tmpAttributes);
1047 if (face->tmpLogClusters)
1048 free(face->tmpLogClusters);
1049 free(face);
1050 }
1051
HB_SelectScript(HB_ShaperItem * shaper_item,const HB_OpenTypeFeature * features)1052 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1053 {
1054 HB_Script script = shaper_item->item.script;
1055
1056 if (!shaper_item->face->supported_scripts[script])
1057 return false;
1058
1059 HB_Face face = shaper_item->face;
1060 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1061 return true;
1062
1063 face->current_script = script;
1064 face->current_flags = shaper_item->shaperFlags;
1065
1066 assert(script < HB_ScriptCount);
1067 // find script in our list of supported scripts.
1068 unsigned int tag = ot_scripts[script].tag;
1069
1070 if (face->gsub && features) {
1071 #ifdef OT_DEBUG
1072 {
1073 HB_FeatureList featurelist = face->gsub->FeatureList;
1074 int numfeatures = featurelist.FeatureCount;
1075 DEBUG("gsub table has %d features", numfeatures);
1076 for (int i = 0; i < numfeatures; i++) {
1077 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1078 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1079 }
1080 }
1081 #endif
1082 HB_GSUB_Clear_Features(face->gsub);
1083 HB_UShort script_index;
1084 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1085 if (error == HB_Err_Not_Covered) {
1086 error = HB_GSUB_Select_Script(face->gsub, DefaultScript, &script_index);
1087 }
1088 if (!error) {
1089 DEBUG("script %s has script index %d", tag_to_string(tag), script_index);
1090 while (features->tag) {
1091 HB_UShort feature_index;
1092 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1093 if (!error) {
1094 DEBUG(" adding feature %s", tag_to_string(features->tag));
1095 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1096 }
1097 ++features;
1098 }
1099 }
1100 }
1101
1102 // reset
1103 face->has_opentype_kerning = false;
1104
1105 if (face->gpos) {
1106 HB_GPOS_Clear_Features(face->gpos);
1107 HB_UShort script_index;
1108 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1109 if (error == HB_Err_Not_Covered) {
1110 error = HB_GPOS_Select_Script(face->gpos, DefaultScript, &script_index);
1111 }
1112 if (!error) {
1113 #ifdef OT_DEBUG
1114 {
1115 HB_FeatureList featurelist = face->gpos->FeatureList;
1116 int numfeatures = featurelist.FeatureCount;
1117 DEBUG("gpos table has %d features", numfeatures);
1118 for(int i = 0; i < numfeatures; i++) {
1119 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1120 HB_UShort feature_index;
1121 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1122 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1123 }
1124 }
1125 #endif
1126 HB_UInt *feature_tag_list_buffer;
1127 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1128 if (!error) {
1129 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1130 while (*feature_tag_list) {
1131 HB_UShort feature_index;
1132 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1133 if (face->current_flags & HB_ShaperFlag_NoKerning) {
1134 ++feature_tag_list;
1135 continue;
1136 }
1137 face->has_opentype_kerning = true;
1138 }
1139 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1140 if (!error)
1141 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1142 ++feature_tag_list;
1143 }
1144 FREE(feature_tag_list_buffer);
1145 }
1146 }
1147 }
1148
1149 return true;
1150 }
1151
HB_OpenTypeShape(HB_ShaperItem * item,const hb_uint32 * properties)1152 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1153 {
1154 HB_GlyphAttributes *tmpAttributes;
1155 unsigned int *tmpLogClusters;
1156
1157 HB_Face face = item->face;
1158
1159 face->length = item->num_glyphs;
1160
1161 hb_buffer_clear(face->buffer);
1162
1163 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1164 if (!tmpAttributes)
1165 return false;
1166 face->tmpAttributes = tmpAttributes;
1167
1168 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1169 if (!tmpLogClusters)
1170 return false;
1171 face->tmpLogClusters = tmpLogClusters;
1172
1173 for (int i = 0; i < face->length; ++i) {
1174 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1175 face->tmpAttributes[i] = item->attributes[i];
1176 face->tmpLogClusters[i] = item->log_clusters[i];
1177 }
1178
1179 #ifdef OT_DEBUG
1180 DEBUG("-----------------------------------------");
1181 // DEBUG("log clusters before shaping:");
1182 // for (int j = 0; j < length; j++)
1183 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1184 DEBUG("original glyphs: %p", item->glyphs);
1185 for (int i = 0; i < length; ++i)
1186 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1187 // dump_string(hb_buffer);
1188 #endif
1189
1190 face->glyphs_substituted = false;
1191 if (face->gsub) {
1192 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1193 if (error && error != HB_Err_Not_Covered)
1194 return false;
1195 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1196 }
1197
1198 #ifdef OT_DEBUG
1199 // DEBUG("log clusters before shaping:");
1200 // for (int j = 0; j < length; j++)
1201 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1202 DEBUG("shaped glyphs:");
1203 for (int i = 0; i < length; ++i)
1204 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1205 DEBUG("-----------------------------------------");
1206 // dump_string(hb_buffer);
1207 #endif
1208
1209 return true;
1210 }
1211
HB_OpenTypePosition(HB_ShaperItem * item,int availableGlyphs,HB_Bool doLogClusters)1212 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1213 {
1214 HB_Face face = item->face;
1215
1216 bool glyphs_positioned = false;
1217 if (face->gpos) {
1218 if (face->buffer->positions)
1219 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1220 // #### check that passing "false,false" is correct
1221 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1222 }
1223
1224 if (!face->glyphs_substituted && !glyphs_positioned) {
1225 HB_GetGlyphAdvances(item);
1226 return true; // nothing to do for us
1227 }
1228
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;
1233 }
1234
1235 HB_Glyph *glyphs = item->glyphs;
1236 HB_GlyphAttributes *attributes = item->attributes;
1237
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;
1243 }
1244 item->num_glyphs = face->buffer->in_length;
1245
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;
1262 }
1263 }
1264 for (int j = oldCi; j < face->length; j++)
1265 logClusters[j] = clusterStart;
1266 }
1267
1268 // calulate the advances for the shaped glyphs
1269 // DEBUG("unpositioned: ");
1270
1271 // positioning code:
1272 if (glyphs_positioned) {
1273 HB_GetGlyphAdvances(item);
1274 HB_Position positions = face->buffer->positions;
1275 HB_Fixed *advances = item->advances;
1276
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);
1284
1285 HB_Fixed adjustment = positions[i].x_advance;
1286
1287 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1288 adjustment = HB_FIXED_ROUND(adjustment);
1289
1290 if (positions[i].new_advance) {
1291 advances[i] = adjustment;
1292 } else {
1293 advances[i] += adjustment;
1294 }
1295
1296 int back = 0;
1297 HB_FixedPoint *offsets = item->offsets;
1298 offsets[i].x = positions[i].x_pos;
1299 offsets[i].y = positions[i].y_pos;
1300 while (positions[i - back].back) {
1301 back += positions[i - back].back;
1302 offsets[i].x += positions[i - back].x_pos;
1303 offsets[i].y += positions[i - back].y_pos;
1304 }
1305 offsets[i].y = -offsets[i].y;
1306
1307 if (item->item.bidiLevel % 2) {
1308 // ### may need to go back multiple glyphs like in ltr
1309 back = positions[i].back;
1310 while (back--)
1311 offsets[i].x -= advances[i-back];
1312 } else {
1313 back = 0;
1314 while (positions[i - back].back) {
1315 back += positions[i - back].back;
1316 offsets[i].x -= advances[i-back];
1317 }
1318 }
1319 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1320 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1321 DEBUG("Glyph offset[%d] x:%d, y: %d)", i, offsets[i].x, offsets[i].y);
1322 }
1323 item->kerning_applied = face->has_opentype_kerning;
1324 } else {
1325 HB_HeuristicPosition(item);
1326 }
1327
1328 #ifdef OT_DEBUG
1329 if (doLogClusters) {
1330 DEBUG("log clusters after shaping:");
1331 for (int j = 0; j < length; j++)
1332 DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1333 }
1334 DEBUG("final glyphs:");
1335 for (int i = 0; i < (int)hb_buffer->in_length; ++i)
1336 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
1337 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
1338 glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
1339 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1340 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1341 DEBUG("-----------------------------------------");
1342 #endif
1343 return true;
1344 }
1345
HB_ShapeItem(HB_ShaperItem * shaper_item)1346 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1347 {
1348 HB_Bool result = false;
1349 if (shaper_item->num_glyphs < shaper_item->item.length) {
1350 shaper_item->num_glyphs = shaper_item->item.length;
1351 return false;
1352 }
1353 assert(shaper_item->item.script < HB_ScriptCount);
1354 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1355 shaper_item->glyphIndicesPresent = false;
1356 return result;
1357 }
1358
1359