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
28 #include "harfbuzz-stream-private.h"
29 #include <assert.h>
30 #include <stdio.h>
31
32 #define HB_MIN(a, b) ((a) < (b) ? (a) : (b))
33 #define HB_MAX(a, b) ((a) > (b) ? (a) : (b))
34
35 // -----------------------------------------------------------------------------------------------------
36 //
37 // The line break algorithm. See http://www.unicode.org/reports/tr14/tr14-13.html
38 //
39 // -----------------------------------------------------------------------------------------------------
40
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:
44
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
61 */
62
63 // The following line break classes are not treated by the table:
64 // AI, BK, CB, CR, LF, NL, SA, SG, SP, XX
65
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
73 };
74 #define DB DirectBreak
75 #define IB IndirectBreak
76 #define CI CombiningIndirectBreak
77 #define CP CombiningProhibitedBreak
78 #define PB ProhibitedBreak
79
80 static const hb_uint8 breakTable[HB_LineBreak_JT+1][HB_LineBreak_JT+1] =
81 {
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 }
109 };
110 #undef DB
111 #undef IB
112 #undef CI
113 #undef CP
114 #undef PB
115
116 static const hb_uint8 graphemeTable[HB_Grapheme_LVT + 1][HB_Grapheme_LVT + 1] =
117 {
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
129 };
130
calcLineBreaks(const HB_UChar16 * uc,hb_uint32 len,HB_CharAttributes * charAttributes)131 static void calcLineBreaks(const HB_UChar16 *uc, hb_uint32 len, HB_CharAttributes *charAttributes)
132 {
133 if (!len)
134 return;
135
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;
143
144 charAttributes[0].whiteSpace = (cls == HB_LineBreak_SP || cls == HB_LineBreak_BK);
145 charAttributes[0].charStop = true;
146
147 int lcls = cls;
148 for (hb_uint32 i = 1; i < len; ++i) {
149 charAttributes[i].whiteSpace = false;
150 charAttributes[i].charStop = true;
151
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;
167 }
168 }
169
170 // set white space and char stop flag
171 if (ncls >= HB_LineBreak_SP)
172 charAttributes[i].whiteSpace = true;
173
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;
179 }
180
181 if (ncls == HB_LineBreak_SP)
182 goto next_no_cls_update;
183 if (ncls >= HB_LineBreak_CR)
184 goto next;
185
186 {
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;
195
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;
213 }
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;
222 }
223 }
224 next:
225 cls = ncls;
226 next_no_cls_update:
227 lcls = ncls;
228 grapheme = ngrapheme;
229 charAttributes[i-1].lineBreakType = lineBreakType;
230 }
231 charAttributes[len-1].lineBreakType = HB_ForcedBreak;
232 }
233
234 // --------------------------------------------------------------------------------------------------------------------------------------------
235 //
236 // Basic processing
237 //
238 // --------------------------------------------------------------------------------------------------------------------------------------------
239
positionCluster(HB_ShaperItem * item,int gfrom,int glast)240 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast)
241 {
242 int nmarks = glast - gfrom;
243 assert(nmarks > 0);
244
245 HB_Glyph *glyphs = item->glyphs;
246 HB_GlyphAttributes *attributes = item->attributes;
247
248 HB_GlyphMetrics baseMetrics;
249 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics);
250
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;
255
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);
258
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);
267
268 bool rightToLeft = item->item.bidiLevel % 2;
269
270 int i;
271 unsigned char lastCmb = 0;
272 HB_GlyphMetrics attachmentRect;
273 memset(&attachmentRect, 0, sizeof(attachmentRect));
274
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);
282
283 HB_Fixed offset = offsetBase;
284 unsigned char cmb = attributes[gfrom+i].combiningClass;
285
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.
291
292 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes)
293
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
316
317 }
318
319 // combining marks of different class don't interact. Reset the rectangle.
320 if (cmb != lastCmb) {
321 //qDebug("resetting rect");
322 attachmentRect = baseMetrics;
323 }
324
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;
339
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;
369
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;
378
379 case HB_Combining_IotaSubscript:
380 default:
381 break;
382 }
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;
386
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;
393
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;
401 }
402 item->advances[gfrom+i] = 0;
403 }
404 }
405
HB_HeuristicPosition(HB_ShaperItem * item)406 void HB_HeuristicPosition(HB_ShaperItem *item)
407 {
408 HB_GetGlyphAdvances(item);
409 HB_GlyphAttributes *attributes = item->attributes;
410
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;
419 }
420 }
421 }
422
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
HB_HeuristicSetGlyphAttributes(HB_ShaperItem * item)426 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item)
427 {
428 const HB_UChar16 *uc = item->string + item->item.pos;
429 hb_uint32 length = item->item.length;
430
431 // ### zeroWidth and justification are missing here!!!!!
432
433 // BEGIN android-changed
434 // We apply the same fix for Chrome to Android.
435 // Chrome team will talk with upsteam about it.
436 assert(length <= item->num_glyphs);
437 // END android-changed
438
439 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs);
440 HB_GlyphAttributes *attributes = item->attributes;
441 unsigned short *logClusters = item->log_clusters;
442
443 hb_uint32 glyph_pos = 0;
444 hb_uint32 i;
445 for (i = 0; i < length; i++) {
446 if (HB_IsHighSurrogate(uc[i]) && i < length - 1
447 && HB_IsLowSurrogate(uc[i + 1])) {
448 logClusters[i] = glyph_pos;
449 logClusters[++i] = glyph_pos;
450 } else {
451 logClusters[i] = glyph_pos;
452 }
453 ++glyph_pos;
454 }
455
456 // BEGIN android-removed
457 // We apply the same fix for Chrome to Android.
458 // Chrome team will talk with upsteam about it
459 //
460 // assert(glyph_pos == item->num_glyphs);
461 //
462 // END android-removed
463
464 // first char in a run is never (treated as) a mark
465 int cStart = 0;
466 const bool symbolFont = item->face->isSymbolFont;
467 attributes[0].mark = false;
468 attributes[0].clusterStart = true;
469 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]);
470
471 int pos = 0;
472 HB_CharCategory lastCat;
473 int dummy;
474 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy);
475 for (i = 1; i < length; ++i) {
476 if (logClusters[i] == pos)
477 // same glyph
478 continue;
479 ++pos;
480 while (pos < logClusters[i]) {
481 attributes[pos] = attributes[pos-1];
482 ++pos;
483 }
484 // hide soft-hyphens by default
485 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i]))
486 attributes[pos].dontPrint = true;
487 HB_CharCategory cat;
488 int cmb;
489 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb);
490 if (cat != HB_Mark_NonSpacing) {
491 attributes[pos].mark = false;
492 attributes[pos].clusterStart = true;
493 attributes[pos].combiningClass = 0;
494 cStart = logClusters[i];
495 } else {
496 if (cmb == 0) {
497 // Fix 0 combining classes
498 if ((uc[pos] & 0xff00) == 0x0e00) {
499 // thai or lao
500 if (uc[pos] == 0xe31 ||
501 uc[pos] == 0xe34 ||
502 uc[pos] == 0xe35 ||
503 uc[pos] == 0xe36 ||
504 uc[pos] == 0xe37 ||
505 uc[pos] == 0xe47 ||
506 uc[pos] == 0xe4c ||
507 uc[pos] == 0xe4d ||
508 uc[pos] == 0xe4e) {
509 cmb = HB_Combining_AboveRight;
510 } else if (uc[pos] == 0xeb1 ||
511 uc[pos] == 0xeb4 ||
512 uc[pos] == 0xeb5 ||
513 uc[pos] == 0xeb6 ||
514 uc[pos] == 0xeb7 ||
515 uc[pos] == 0xebb ||
516 uc[pos] == 0xecc ||
517 uc[pos] == 0xecd) {
518 cmb = HB_Combining_Above;
519 } else if (uc[pos] == 0xebc) {
520 cmb = HB_Combining_Below;
521 }
522 }
523 }
524
525 attributes[pos].mark = true;
526 attributes[pos].clusterStart = false;
527 attributes[pos].combiningClass = cmb;
528 logClusters[i] = cStart;
529 }
530 // one gets an inter character justification point if the current char is not a non spacing mark.
531 // as then the current char belongs to the last one and one gets a space justification point
532 // after the space char.
533 if (lastCat == HB_Separator_Space)
534 attributes[pos-1].justification = HB_Space;
535 else if (cat != HB_Mark_NonSpacing)
536 attributes[pos-1].justification = HB_Character;
537 else
538 attributes[pos-1].justification = HB_NoJustification;
539
540 lastCat = cat;
541 }
542 pos = logClusters[length-1];
543 if (lastCat == HB_Separator_Space)
544 attributes[pos].justification = HB_Space;
545 else
546 attributes[pos].justification = HB_Character;
547 }
548
549 #ifndef NO_OPENTYPE
550 static const HB_OpenTypeFeature basic_features[] = {
551 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty },
552 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty },
553 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty },
554 {0, 0}
555 };
556 #endif
557
HB_ConvertStringToGlyphIndices(HB_ShaperItem * shaper_item)558 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item)
559 {
560 if (shaper_item->glyphIndicesPresent) {
561 shaper_item->num_glyphs = shaper_item->initialGlyphCount;
562 shaper_item->glyphIndicesPresent = false;
563 return true;
564 }
565 return shaper_item->font->klass
566 ->convertStringToGlyphIndices(shaper_item->font,
567 shaper_item->string + shaper_item->item.pos, shaper_item->item.length,
568 shaper_item->glyphs, &shaper_item->num_glyphs,
569 shaper_item->item.bidiLevel % 2);
570 }
571
HB_BasicShape(HB_ShaperItem * shaper_item)572 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item)
573 {
574 #ifndef NO_OPENTYPE
575 const int availableGlyphs = shaper_item->num_glyphs;
576 #endif
577
578 if (!HB_ConvertStringToGlyphIndices(shaper_item))
579 return false;
580
581 HB_HeuristicSetGlyphAttributes(shaper_item);
582
583 #ifndef NO_OPENTYPE
584 if (HB_SelectScript(shaper_item, basic_features)) {
585 HB_OpenTypeShape(shaper_item, /*properties*/0);
586 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true);
587 }
588 #endif
589
590 HB_HeuristicPosition(shaper_item);
591 return true;
592 }
593
594 const HB_ScriptEngine HB_ScriptEngines[] = {
595 // Common
596 { HB_BasicShape, 0},
597 // Greek
598 { HB_GreekShape, 0},
599 // Cyrillic
600 { HB_BasicShape, 0},
601 // Armenian
602 { HB_BasicShape, 0},
603 // Hebrew
604 { HB_HebrewShape, 0 },
605 // Arabic
606 { HB_ArabicShape, 0},
607 // Syriac
608 { HB_ArabicShape, 0},
609 // Thaana
610 { HB_BasicShape, 0 },
611 // Devanagari
612 { HB_IndicShape, HB_IndicAttributes },
613 // Bengali
614 { HB_IndicShape, HB_IndicAttributes },
615 // Gurmukhi
616 { HB_IndicShape, HB_IndicAttributes },
617 // Gujarati
618 { HB_IndicShape, HB_IndicAttributes },
619 // Oriya
620 { HB_IndicShape, HB_IndicAttributes },
621 // Tamil
622 { HB_IndicShape, HB_IndicAttributes },
623 // Telugu
624 { HB_IndicShape, HB_IndicAttributes },
625 // Kannada
626 { HB_IndicShape, HB_IndicAttributes },
627 // Malayalam
628 { HB_IndicShape, HB_IndicAttributes },
629 // Sinhala
630 { HB_IndicShape, HB_IndicAttributes },
631 // Thai
632 { HB_BasicShape, HB_ThaiAttributes },
633 // Lao
634 { HB_BasicShape, 0 },
635 // Tibetan
636 { HB_TibetanShape, HB_TibetanAttributes },
637 // Myanmar
638 { HB_MyanmarShape, HB_MyanmarAttributes },
639 // Georgian
640 { HB_BasicShape, 0 },
641 // Hangul
642 { HB_HangulShape, 0 },
643 // Ogham
644 { HB_BasicShape, 0 },
645 // Runic
646 { HB_BasicShape, 0 },
647 // Khmer
648 { HB_KhmerShape, HB_KhmerAttributes },
649 // N'Ko
650 { HB_ArabicShape, 0}
651 };
652
HB_GetCharAttributes(const HB_UChar16 * string,hb_uint32 stringLength,const HB_ScriptItem * items,hb_uint32 numItems,HB_CharAttributes * attributes)653 void HB_GetCharAttributes(const HB_UChar16 *string, hb_uint32 stringLength,
654 const HB_ScriptItem *items, hb_uint32 numItems,
655 HB_CharAttributes *attributes)
656 {
657 calcLineBreaks(string, stringLength, attributes);
658
659 for (hb_uint32 i = 0; i < numItems; ++i) {
660 HB_Script script = items[i].script;
661 if (script == HB_Script_Inherited)
662 script = HB_Script_Common;
663 HB_AttributeFunction attributeFunction = HB_ScriptEngines[script].charAttributes;
664 if (!attributeFunction)
665 continue;
666 attributeFunction(script, string, items[i].pos, items[i].length, attributes);
667 }
668 }
669
670
671 enum BreakRule { NoBreak = 0, Break = 1, Middle = 2 };
672
673 static const hb_uint8 wordbreakTable[HB_Word_ExtendNumLet + 1][HB_Word_ExtendNumLet + 1] = {
674 // Other Format Katakana ALetter MidLetter MidNum Numeric ExtendNumLet
675 { Break, Break, Break, Break, Break, Break, Break, Break }, // Other
676 { Break, Break, Break, Break, Break, Break, Break, Break }, // Format
677 { Break, Break, NoBreak, Break, Break, Break, Break, NoBreak }, // Katakana
678 { Break, Break, Break, NoBreak, Middle, Break, NoBreak, NoBreak }, // ALetter
679 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidLetter
680 { Break, Break, Break, Break, Break, Break, Break, Break }, // MidNum
681 { Break, Break, Break, NoBreak, Break, Middle, NoBreak, NoBreak }, // Numeric
682 { Break, Break, NoBreak, NoBreak, Break, Break, NoBreak, NoBreak }, // ExtendNumLet
683 };
684
HB_GetWordBoundaries(const HB_UChar16 * string,hb_uint32 stringLength,const HB_ScriptItem *,hb_uint32,HB_CharAttributes * attributes)685 void HB_GetWordBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
686 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
687 HB_CharAttributes *attributes)
688 {
689 if (stringLength == 0)
690 return;
691 unsigned int brk = HB_GetWordClass(string[0]);
692 attributes[0].wordBoundary = true;
693 for (hb_uint32 i = 1; i < stringLength; ++i) {
694 if (!attributes[i].charStop) {
695 attributes[i].wordBoundary = false;
696 continue;
697 }
698 hb_uint32 nbrk = HB_GetWordClass(string[i]);
699 if (nbrk == HB_Word_Format) {
700 attributes[i].wordBoundary = (HB_GetSentenceClass(string[i-1]) == HB_Sentence_Sep);
701 continue;
702 }
703 BreakRule rule = (BreakRule)wordbreakTable[brk][nbrk];
704 if (rule == Middle) {
705 rule = Break;
706 hb_uint32 lookahead = i + 1;
707 while (lookahead < stringLength) {
708 hb_uint32 testbrk = HB_GetWordClass(string[lookahead]);
709 if (testbrk == HB_Word_Format && HB_GetSentenceClass(string[lookahead]) != HB_Sentence_Sep) {
710 ++lookahead;
711 continue;
712 }
713 if (testbrk == brk) {
714 rule = NoBreak;
715 while (i < lookahead)
716 attributes[i++].wordBoundary = false;
717 nbrk = testbrk;
718 }
719 break;
720 }
721 }
722 attributes[i].wordBoundary = (rule == Break);
723 brk = nbrk;
724 }
725 }
726
727
728 enum SentenceBreakStates {
729 SB_Initial,
730 SB_Upper,
731 SB_UpATerm,
732 SB_ATerm,
733 SB_ATermC,
734 SB_ACS,
735 SB_STerm,
736 SB_STermC,
737 SB_SCS,
738 SB_BAfter,
739 SB_Break,
740 SB_Look
741 };
742
743 static const hb_uint8 sentenceBreakTable[HB_Sentence_Close + 1][HB_Sentence_Close + 1] = {
744 // Other Sep Format Sp Lower Upper OLetter Numeric ATerm STerm Close
745 { SB_Initial, SB_BAfter , SB_Initial, SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_ATerm , SB_STerm , SB_Initial }, // SB_Initial,
746 { SB_Initial, SB_BAfter , SB_Upper , SB_Initial, SB_Initial, SB_Upper , SB_Initial, SB_Initial, SB_UpATerm, SB_STerm , SB_Initial }, // SB_Upper
747
748 { SB_Look , SB_BAfter , SB_UpATerm, SB_ACS , SB_Initial, SB_Upper , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_UpATerm
749 { SB_Look , SB_BAfter , SB_ATerm , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Initial, SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATerm
750 { SB_Look , SB_BAfter , SB_ATermC , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_ATermC }, // SB_ATermC,
751 { SB_Look , SB_BAfter , SB_ACS , SB_ACS , SB_Initial, SB_Break , SB_Break , SB_Look , SB_ATerm , SB_STerm , SB_Look }, // SB_ACS,
752
753 { SB_Break , SB_BAfter , SB_STerm , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STerm,
754 { SB_Break , SB_BAfter , SB_STermC , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_STermC }, // SB_STermC,
755 { SB_Break , SB_BAfter , SB_SCS , SB_SCS , SB_Break , SB_Break , SB_Break , SB_Break , SB_ATerm , SB_STerm , SB_Break }, // SB_SCS,
756 { SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break , SB_Break }, // SB_BAfter,
757 };
758
HB_GetSentenceBoundaries(const HB_UChar16 * string,hb_uint32 stringLength,const HB_ScriptItem *,hb_uint32,HB_CharAttributes * attributes)759 void HB_GetSentenceBoundaries(const HB_UChar16 *string, hb_uint32 stringLength,
760 const HB_ScriptItem * /*items*/, hb_uint32 /*numItems*/,
761 HB_CharAttributes *attributes)
762 {
763 if (stringLength == 0)
764 return;
765 hb_uint32 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[0])];
766 attributes[0].sentenceBoundary = true;
767 for (hb_uint32 i = 1; i < stringLength; ++i) {
768 if (!attributes[i].charStop) {
769 attributes[i].sentenceBoundary = false;
770 continue;
771 }
772 brk = sentenceBreakTable[brk][HB_GetSentenceClass(string[i])];
773 if (brk == SB_Look) {
774 brk = SB_Break;
775 hb_uint32 lookahead = i + 1;
776 while (lookahead < stringLength) {
777 hb_uint32 sbrk = HB_GetSentenceClass(string[lookahead]);
778 if (sbrk != HB_Sentence_Other && sbrk != HB_Sentence_Numeric && sbrk != HB_Sentence_Close) {
779 break;
780 } else if (sbrk == HB_Sentence_Lower) {
781 brk = SB_Initial;
782 break;
783 }
784 ++lookahead;
785 }
786 if (brk == SB_Initial) {
787 while (i < lookahead)
788 attributes[i++].sentenceBoundary = false;
789 }
790 }
791 if (brk == SB_Break) {
792 attributes[i].sentenceBoundary = true;
793 brk = sentenceBreakTable[SB_Initial][HB_GetSentenceClass(string[i])];
794 } else {
795 attributes[i].sentenceBoundary = false;
796 }
797 }
798 }
799
800
tag_to_string(HB_UInt tag)801 static inline char *tag_to_string(HB_UInt tag)
802 {
803 static char string[5];
804 string[0] = (tag >> 24)&0xff;
805 string[1] = (tag >> 16)&0xff;
806 string[2] = (tag >> 8)&0xff;
807 string[3] = tag&0xff;
808 string[4] = 0;
809 return string;
810 }
811
812 #ifdef OT_DEBUG
dump_string(HB_Buffer buffer)813 static void dump_string(HB_Buffer buffer)
814 {
815 for (uint i = 0; i < buffer->in_length; ++i) {
816 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster);
817 }
818 }
819 #define DEBUG printf
820 #else
821 #define DEBUG if (1) ; else printf
822 #endif
823
824 #define DefaultLangSys 0xffff
825 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T')
826
827 enum {
828 RequiresGsub = 1,
829 RequiresGpos = 2
830 };
831
832 struct OTScripts {
833 unsigned int tag;
834 int flags;
835 };
836 static const OTScripts ot_scripts [] = {
837 // Common
838 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 },
839 // Greek
840 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 },
841 // Cyrillic
842 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 },
843 // Armenian
844 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 },
845 // Hebrew
846 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 },
847 // Arabic
848 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 },
849 // Syriac
850 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 },
851 // Thaana
852 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 },
853 // Devanagari
854 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 },
855 // Bengali
856 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 },
857 // Gurmukhi
858 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 },
859 // Gujarati
860 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 },
861 // Oriya
862 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 },
863 // Tamil
864 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 },
865 // Telugu
866 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 },
867 // Kannada
868 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 },
869 // Malayalam
870 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 },
871 // Sinhala
872 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 },
873 // Thai
874 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 },
875 // Lao
876 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 },
877 // Tibetan
878 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 },
879 // Myanmar
880 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 },
881 // Georgian
882 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 },
883 // Hangul
884 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 },
885 // Ogham
886 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 },
887 // Runic
888 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 },
889 // Khmer
890 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 },
891 // N'Ko
892 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 }
893 };
894 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) };
895
checkScript(HB_Face face,int script)896 static HB_Bool checkScript(HB_Face face, int script)
897 {
898 assert(script < HB_ScriptCount);
899
900 if (!face->gsub && !face->gpos)
901 return false;
902
903 unsigned int tag = ot_scripts[script].tag;
904 int requirements = ot_scripts[script].flags;
905
906 if (requirements & RequiresGsub) {
907 if (!face->gsub)
908 return false;
909
910 HB_UShort script_index;
911 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
912 if (error) {
913 DEBUG("could not select script %d in GSub table: %d", (int)script, error);
914 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
915 if (error)
916 return false;
917 }
918 }
919
920 if (requirements & RequiresGpos) {
921 if (!face->gpos)
922 return false;
923
924 HB_UShort script_index;
925 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index);
926 if (error) {
927 DEBUG("could not select script in gpos table: %d", error);
928 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index);
929 if (error)
930 return false;
931 }
932
933 }
934 return true;
935 }
936
getTableStream(void * font,HB_GetFontTableFunc tableFunc,HB_Tag tag)937 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag)
938 {
939 HB_Error error;
940 HB_UInt length = 0;
941 HB_Stream stream = 0;
942
943 if (!font)
944 return 0;
945
946 error = tableFunc(font, tag, 0, &length);
947 if (error)
948 return 0;
949 stream = (HB_Stream)malloc(sizeof(HB_StreamRec));
950 if (!stream)
951 return 0;
952 stream->base = (HB_Byte*)malloc(length);
953 if (!stream->base) {
954 free(stream);
955 return 0;
956 }
957 error = tableFunc(font, tag, stream->base, &length);
958 if (error) {
959 _hb_close_stream(stream);
960 return 0;
961 }
962 stream->size = length;
963 stream->pos = 0;
964 stream->cursor = NULL;
965 return stream;
966 }
967
HB_NewFace(void * font,HB_GetFontTableFunc tableFunc)968 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc)
969 {
970 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec));
971 if (!face)
972 return 0;
973
974 face->isSymbolFont = false;
975 face->gdef = 0;
976 face->gpos = 0;
977 face->gsub = 0;
978 face->current_script = HB_ScriptCount;
979 face->current_flags = HB_ShaperFlag_Default;
980 face->has_opentype_kerning = false;
981 face->tmpAttributes = 0;
982 face->tmpLogClusters = 0;
983 face->glyphs_substituted = false;
984 face->buffer = 0;
985
986 HB_Error error = HB_Err_Ok;
987 HB_Stream stream;
988 HB_Stream gdefStream;
989
990 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF);
991 error = HB_Err_Not_Covered;
992 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) {
993 //DEBUG("error loading gdef table: %d", error);
994 face->gdef = 0;
995 }
996
997 //DEBUG() << "trying to load gsub table";
998 stream = getTableStream(font, tableFunc, TTAG_GSUB);
999 error = HB_Err_Not_Covered;
1000 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) {
1001 face->gsub = 0;
1002 if (error != HB_Err_Not_Covered) {
1003 //DEBUG("error loading gsub table: %d", error);
1004 } else {
1005 //DEBUG("face doesn't have a gsub table");
1006 }
1007 }
1008 _hb_close_stream(stream);
1009
1010 stream = getTableStream(font, tableFunc, TTAG_GPOS);
1011 error = HB_Err_Not_Covered;
1012 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) {
1013 face->gpos = 0;
1014 DEBUG("error loading gpos table: %d", error);
1015 }
1016 _hb_close_stream(stream);
1017
1018 _hb_close_stream(gdefStream);
1019
1020 for (unsigned int i = 0; i < HB_ScriptCount; ++i)
1021 face->supported_scripts[i] = checkScript(face, i);
1022
1023 if (hb_buffer_new(&face->buffer) != HB_Err_Ok) {
1024 HB_FreeFace(face);
1025 return 0;
1026 }
1027
1028 return face;
1029 }
1030
HB_FreeFace(HB_Face face)1031 void HB_FreeFace(HB_Face face)
1032 {
1033 if (!face)
1034 return;
1035 if (face->gpos)
1036 HB_Done_GPOS_Table(face->gpos);
1037 if (face->gsub)
1038 HB_Done_GSUB_Table(face->gsub);
1039 if (face->gdef)
1040 HB_Done_GDEF_Table(face->gdef);
1041 if (face->buffer)
1042 hb_buffer_free(face->buffer);
1043 if (face->tmpAttributes)
1044 free(face->tmpAttributes);
1045 if (face->tmpLogClusters)
1046 free(face->tmpLogClusters);
1047 free(face);
1048 }
1049
HB_SelectScript(HB_ShaperItem * shaper_item,const HB_OpenTypeFeature * features)1050 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features)
1051 {
1052 HB_Script script = shaper_item->item.script;
1053
1054 if (!shaper_item->face->supported_scripts[script])
1055 return false;
1056
1057 HB_Face face = shaper_item->face;
1058 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags)
1059 return true;
1060
1061 face->current_script = script;
1062 face->current_flags = shaper_item->shaperFlags;
1063
1064 assert(script < HB_ScriptCount);
1065 // find script in our list of supported scripts.
1066 unsigned int tag = ot_scripts[script].tag;
1067
1068 if (face->gsub && features) {
1069 #ifdef OT_DEBUG
1070 {
1071 HB_FeatureList featurelist = face->gsub->FeatureList;
1072 int numfeatures = featurelist.FeatureCount;
1073 DEBUG("gsub table has %d features", numfeatures);
1074 for (int i = 0; i < numfeatures; i++) {
1075 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1076 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1077 }
1078 }
1079 #endif
1080 HB_GSUB_Clear_Features(face->gsub);
1081 HB_UShort script_index;
1082 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index);
1083 if (!error) {
1084 DEBUG("script %s has script index %d", tag_to_string(script), script_index);
1085 while (features->tag) {
1086 HB_UShort feature_index;
1087 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index);
1088 if (!error) {
1089 DEBUG(" adding feature %s", tag_to_string(features->tag));
1090 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property);
1091 }
1092 ++features;
1093 }
1094 }
1095 }
1096
1097 // reset
1098 face->has_opentype_kerning = false;
1099
1100 if (face->gpos) {
1101 HB_GPOS_Clear_Features(face->gpos);
1102 HB_UShort script_index;
1103 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index);
1104 if (!error) {
1105 #ifdef OT_DEBUG
1106 {
1107 HB_FeatureList featurelist = face->gpos->FeatureList;
1108 int numfeatures = featurelist.FeatureCount;
1109 DEBUG("gpos table has %d features", numfeatures);
1110 for(int i = 0; i < numfeatures; i++) {
1111 HB_FeatureRecord *r = featurelist.FeatureRecord + i;
1112 HB_UShort feature_index;
1113 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index);
1114 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag));
1115 }
1116 }
1117 #endif
1118 HB_UInt *feature_tag_list_buffer;
1119 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer);
1120 if (!error) {
1121 HB_UInt *feature_tag_list = feature_tag_list_buffer;
1122 while (*feature_tag_list) {
1123 HB_UShort feature_index;
1124 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) {
1125 if (face->current_flags & HB_ShaperFlag_NoKerning) {
1126 ++feature_tag_list;
1127 continue;
1128 }
1129 face->has_opentype_kerning = true;
1130 }
1131 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index);
1132 if (!error)
1133 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties);
1134 ++feature_tag_list;
1135 }
1136 FREE(feature_tag_list_buffer);
1137 }
1138 }
1139 }
1140
1141 return true;
1142 }
1143
HB_OpenTypeShape(HB_ShaperItem * item,const hb_uint32 * properties)1144 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties)
1145 {
1146 HB_GlyphAttributes *tmpAttributes;
1147 unsigned int *tmpLogClusters;
1148
1149 HB_Face face = item->face;
1150
1151 face->length = item->num_glyphs;
1152
1153 hb_buffer_clear(face->buffer);
1154
1155 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes));
1156 if (!tmpAttributes)
1157 return false;
1158 face->tmpAttributes = tmpAttributes;
1159
1160 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int));
1161 if (!tmpLogClusters)
1162 return false;
1163 face->tmpLogClusters = tmpLogClusters;
1164
1165 for (int i = 0; i < face->length; ++i) {
1166 hb_buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i);
1167 face->tmpAttributes[i] = item->attributes[i];
1168 face->tmpLogClusters[i] = item->log_clusters[i];
1169 }
1170
1171 #ifdef OT_DEBUG
1172 DEBUG("-----------------------------------------");
1173 // DEBUG("log clusters before shaping:");
1174 // for (int j = 0; j < length; j++)
1175 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1176 DEBUG("original glyphs: %p", item->glyphs);
1177 for (int i = 0; i < length; ++i)
1178 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1179 // dump_string(hb_buffer);
1180 #endif
1181
1182 face->glyphs_substituted = false;
1183 if (face->gsub) {
1184 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer);
1185 if (error && error != HB_Err_Not_Covered)
1186 return false;
1187 face->glyphs_substituted = (error != HB_Err_Not_Covered);
1188 }
1189
1190 #ifdef OT_DEBUG
1191 // DEBUG("log clusters before shaping:");
1192 // for (int j = 0; j < length; j++)
1193 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1194 DEBUG("shaped glyphs:");
1195 for (int i = 0; i < length; ++i)
1196 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex);
1197 DEBUG("-----------------------------------------");
1198 // dump_string(hb_buffer);
1199 #endif
1200
1201 return true;
1202 }
1203
HB_OpenTypePosition(HB_ShaperItem * item,int availableGlyphs,HB_Bool doLogClusters)1204 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters)
1205 {
1206 HB_Face face = item->face;
1207
1208 bool glyphs_positioned = false;
1209 if (face->gpos) {
1210 if (face->buffer->positions)
1211 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec));
1212 // #### check that passing "false,false" is correct
1213 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered;
1214 }
1215
1216 if (!face->glyphs_substituted && !glyphs_positioned) {
1217 HB_GetGlyphAdvances(item);
1218 return true; // nothing to do for us
1219 }
1220
1221 // make sure we have enough space to write everything back
1222 if (availableGlyphs < (int)face->buffer->in_length) {
1223 item->num_glyphs = face->buffer->in_length;
1224 return false;
1225 }
1226
1227 HB_Glyph *glyphs = item->glyphs;
1228 HB_GlyphAttributes *attributes = item->attributes;
1229
1230 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1231 glyphs[i] = face->buffer->in_string[i].gindex;
1232 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster];
1233 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster)
1234 attributes[i].clusterStart = false;
1235 }
1236 item->num_glyphs = face->buffer->in_length;
1237
1238 if (doLogClusters && face->glyphs_substituted) {
1239 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper.
1240 unsigned short *logClusters = item->log_clusters;
1241 int clusterStart = 0;
1242 int oldCi = 0;
1243 // #### the reconstruction of the logclusters currently does not work if the original string
1244 // contains surrogate pairs
1245 for (unsigned int i = 0; i < face->buffer->in_length; ++i) {
1246 int ci = face->buffer->in_string[i].cluster;
1247 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d",
1248 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart);
1249 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) {
1250 for (int j = oldCi; j < ci; j++)
1251 logClusters[j] = clusterStart;
1252 clusterStart = i;
1253 oldCi = ci;
1254 }
1255 }
1256 for (int j = oldCi; j < face->length; j++)
1257 logClusters[j] = clusterStart;
1258 }
1259
1260 // calulate the advances for the shaped glyphs
1261 // DEBUG("unpositioned: ");
1262
1263 // positioning code:
1264 if (glyphs_positioned) {
1265 HB_GetGlyphAdvances(item);
1266 HB_Position positions = face->buffer->positions;
1267 HB_Fixed *advances = item->advances;
1268
1269 // DEBUG("positioned glyphs:");
1270 for (unsigned int i = 0; i < face->buffer->in_length; i++) {
1271 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i,
1272 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1273 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6),
1274 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6),
1275 // positions[i].back, positions[i].new_advance);
1276
1277 HB_Fixed adjustment = (item->item.bidiLevel % 2) ? -positions[i].x_advance : positions[i].x_advance;
1278
1279 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics))
1280 adjustment = HB_FIXED_ROUND(adjustment);
1281
1282 if (positions[i].new_advance) {
1283 advances[i] = adjustment;
1284 } else {
1285 advances[i] += adjustment;
1286 }
1287
1288 int back = 0;
1289 HB_FixedPoint *offsets = item->offsets;
1290 offsets[i].x = positions[i].x_pos;
1291 offsets[i].y = positions[i].y_pos;
1292 while (positions[i - back].back) {
1293 back += positions[i - back].back;
1294 offsets[i].x += positions[i - back].x_pos;
1295 offsets[i].y += positions[i - back].y_pos;
1296 }
1297 offsets[i].y = -offsets[i].y;
1298
1299 if (item->item.bidiLevel % 2) {
1300 // ### may need to go back multiple glyphs like in ltr
1301 back = positions[i].back;
1302 while (back--)
1303 offsets[i].x -= advances[i-back];
1304 } else {
1305 back = 0;
1306 while (positions[i - back].back) {
1307 back += positions[i - back].back;
1308 offsets[i].x -= advances[i-back];
1309 }
1310 }
1311 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)",
1312 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1313 }
1314 item->kerning_applied = face->has_opentype_kerning;
1315 } else {
1316 HB_HeuristicPosition(item);
1317 }
1318
1319 #ifdef OT_DEBUG
1320 if (doLogClusters) {
1321 DEBUG("log clusters after shaping:");
1322 for (int j = 0; j < length; j++)
1323 DEBUG(" log[%d] = %d", j, item->log_clusters[j]);
1324 }
1325 DEBUG("final glyphs:");
1326 for (int i = 0; i < (int)hb_buffer->in_length; ++i)
1327 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d",
1328 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark,
1329 glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart,
1330 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(),
1331 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt());
1332 DEBUG("-----------------------------------------");
1333 #endif
1334 return true;
1335 }
1336
HB_ShapeItem(HB_ShaperItem * shaper_item)1337 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item)
1338 {
1339 HB_Bool result = false;
1340 if (shaper_item->num_glyphs < shaper_item->item.length) {
1341 shaper_item->num_glyphs = shaper_item->item.length;
1342 return false;
1343 }
1344 assert(shaper_item->item.script < HB_ScriptCount);
1345 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item);
1346 shaper_item->glyphIndicesPresent = false;
1347 return result;
1348 }
1349
1350