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