source: trunk/src/3rdparty/harfbuzz/src/harfbuzz-shaper.cpp@ 561

Last change on this file since 561 was 561, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.1 sources.

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