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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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. */ 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 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 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