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