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