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 // Basic processing 38 // 39 // -------------------------------------------------------------------------------------------------------------------------------------------- 40 41 static inline void positionCluster(HB_ShaperItem *item, int gfrom, int glast) 42 { 43 int nmarks = glast - gfrom; 44 assert(nmarks > 0); 45 46 HB_Glyph *glyphs = item->glyphs; 47 HB_GlyphAttributes *attributes = item->attributes; 48 49 HB_GlyphMetrics baseMetrics; 50 item->font->klass->getGlyphMetrics(item->font, glyphs[gfrom], &baseMetrics); 51 52 if (item->item.script == HB_Script_Hebrew 53 && (-baseMetrics.y) > baseMetrics.height) 54 // we need to attach below the baseline, because of the hebrew iud. 55 baseMetrics.height = -baseMetrics.y; 56 57 // qDebug("---> positionCluster: cluster from %d to %d", gfrom, glast); 58 // qDebug("baseInfo: %f/%f (%f/%f) off=%f/%f", baseInfo.x, baseInfo.y, baseInfo.width, baseInfo.height, baseInfo.xoff, baseInfo.yoff); 59 60 HB_Fixed size = item->font->klass->getFontMetric(item->font, HB_FontAscent) / 10; 61 HB_Fixed offsetBase = HB_FIXED_CONSTANT(1) + (size - HB_FIXED_CONSTANT(4)) / 4; 62 if (size > HB_FIXED_CONSTANT(4)) 63 offsetBase += HB_FIXED_CONSTANT(4); 64 else 65 offsetBase += size; 66 offsetBase = -offsetBase; 67 //qreal offsetBase = (size - 4) / 4 + qMin<qreal>(size, 4) + 1; 68 // qDebug("offset = %f", offsetBase); 69 70 bool rightToLeft = item->item.bidiLevel % 2; 71 72 int i; 73 unsigned char lastCmb = 0; 74 HB_GlyphMetrics attachmentRect; 75 memset(&attachmentRect, 0, sizeof(attachmentRect)); 76 77 for(i = 1; i <= nmarks; i++) { 78 HB_Glyph mark = glyphs[gfrom+i]; 79 HB_GlyphMetrics markMetrics; 80 item->font->klass->getGlyphMetrics(item->font, mark, &markMetrics); 81 HB_FixedPoint p; 82 p.x = p.y = 0; 83 // qDebug("markInfo: %f/%f (%f/%f) off=%f/%f", markInfo.x, markInfo.y, markInfo.width, markInfo.height, markInfo.xoff, markInfo.yoff); 84 85 HB_Fixed offset = offsetBase; 86 unsigned char cmb = attributes[gfrom+i].combiningClass; 87 88 // ### maybe the whole position determination should move down to heuristicSetGlyphAttributes. Would save some 89 // bits in the glyphAttributes structure. 90 if (cmb < 200) { 91 // fixed position classes. We approximate by mapping to one of the others. 92 // currently I added only the ones for arabic, hebrew, lao and thai. 93 94 // for Lao and Thai marks with class 0, see below (heuristicSetGlyphAttributes) 95 96 // add a bit more offset to arabic, a bit hacky 97 if (cmb >= 27 && cmb <= 36 && offset < 3) 98 offset +=1; 99 // below 100 if ((cmb >= 10 && cmb <= 18) || 101 cmb == 20 || cmb == 22 || 102 cmb == 29 || cmb == 32) 103 cmb = HB_Combining_Below; 104 // above 105 else if (cmb == 23 || cmb == 27 || cmb == 28 || 106 cmb == 30 || cmb == 31 || (cmb >= 33 && cmb <= 36)) 107 cmb = HB_Combining_Above; 108 //below-right 109 else if (cmb == 9 || cmb == 103 || cmb == 118) 110 cmb = HB_Combining_BelowRight; 111 // above-right 112 else if (cmb == 24 || cmb == 107 || cmb == 122) 113 cmb = HB_Combining_AboveRight; 114 else if (cmb == 25) 115 cmb = HB_Combining_AboveLeft; 116 // fixed: 117 // 19 21 118 119 } 120 121 // combining marks of different class don't interact. Reset the rectangle. 122 if (cmb != lastCmb) { 123 //qDebug("resetting rect"); 124 attachmentRect = baseMetrics; 125 } 126 127 switch(cmb) { 128 case HB_Combining_DoubleBelow: 129 // ### wrong in rtl context! 130 case HB_Combining_BelowLeft: 131 p.y += offset; 132 case HB_Combining_BelowLeftAttached: 133 p.x += attachmentRect.x - markMetrics.x; 134 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y; 135 break; 136 case HB_Combining_Below: 137 p.y += offset; 138 case HB_Combining_BelowAttached: 139 p.x += attachmentRect.x - markMetrics.x; 140 p.y += (attachmentRect.y + attachmentRect.height) - markMetrics.y; 141 142 p.x += (attachmentRect.width - markMetrics.width) / 2; 143 break; 144 case HB_Combining_BelowRight: 145 p.y += offset; 146 case HB_Combining_BelowRightAttached: 147 p.x += attachmentRect.x + attachmentRect.width - markMetrics.width - markMetrics.x; 148 p.y += attachmentRect.y + attachmentRect.height - markMetrics.y; 149 break; 150 case HB_Combining_Left: 151 p.x -= offset; 152 case HB_Combining_LeftAttached: 153 break; 154 case HB_Combining_Right: 155 p.x += offset; 156 case HB_Combining_RightAttached: 157 break; 158 case HB_Combining_DoubleAbove: 159 // ### wrong in RTL context! 160 case HB_Combining_AboveLeft: 161 p.y -= offset; 162 case HB_Combining_AboveLeftAttached: 163 p.x += attachmentRect.x - markMetrics.x; 164 p.y += attachmentRect.y - markMetrics.y - markMetrics.height; 165 break; 166 case HB_Combining_Above: 167 p.y -= offset; 168 case HB_Combining_AboveAttached: 169 p.x += attachmentRect.x - markMetrics.x; 170 p.y += attachmentRect.y - markMetrics.y - markMetrics.height; 171 172 p.x += (attachmentRect.width - markMetrics.width) / 2; 173 break; 174 case HB_Combining_AboveRight: 175 p.y -= offset; 176 case HB_Combining_AboveRightAttached: 177 p.x += attachmentRect.x + attachmentRect.width - markMetrics.x - markMetrics.width; 178 p.y += attachmentRect.y - markMetrics.y - markMetrics.height; 179 break; 180 181 case HB_Combining_IotaSubscript: 182 default: 183 break; 184 } 185 // qDebug("char=%x combiningClass = %d offset=%f/%f", mark, cmb, p.x(), p.y()); 186 markMetrics.x += p.x; 187 markMetrics.y += p.y; 188 189 HB_GlyphMetrics unitedAttachmentRect = attachmentRect; 190 unitedAttachmentRect.x = HB_MIN(attachmentRect.x, markMetrics.x); 191 unitedAttachmentRect.y = HB_MIN(attachmentRect.y, markMetrics.y); 192 unitedAttachmentRect.width = HB_MAX(attachmentRect.x + attachmentRect.width, markMetrics.x + markMetrics.width) - unitedAttachmentRect.x; 193 unitedAttachmentRect.height = HB_MAX(attachmentRect.y + attachmentRect.height, markMetrics.y + markMetrics.height) - unitedAttachmentRect.y; 194 attachmentRect = unitedAttachmentRect; 195 196 lastCmb = cmb; 197 if (rightToLeft) { 198 item->offsets[gfrom+i].x = p.x; 199 item->offsets[gfrom+i].y = p.y; 200 } else { 201 item->offsets[gfrom+i].x = p.x - baseMetrics.xOffset; 202 item->offsets[gfrom+i].y = p.y - baseMetrics.yOffset; 203 } 204 item->advances[gfrom+i] = 0; 205 } 206 } 207 208 void HB_HeuristicPosition(HB_ShaperItem *item) 209 { 210 HB_GetGlyphAdvances(item); 211 HB_GlyphAttributes *attributes = item->attributes; 212 213 int cEnd = -1; 214 int i = item->num_glyphs; 215 while (i--) { 216 if (cEnd == -1 && attributes[i].mark) { 217 cEnd = i; 218 } else if (cEnd != -1 && !attributes[i].mark) { 219 positionCluster(item, i, cEnd); 220 cEnd = -1; 221 } 222 } 223 } 224 225 // set the glyph attributes heuristically. Assumes a 1 to 1 relationship between chars and glyphs 226 // and no reordering. 227 // also computes logClusters heuristically 228 void HB_HeuristicSetGlyphAttributes(HB_ShaperItem *item) 229 { 230 const HB_UChar16 *uc = item->string + item->item.pos; 231 hb_uint32 length = item->item.length; 232 233 // ### zeroWidth and justification are missing here!!!!! 234 235 assert(item->num_glyphs <= length); 236 237 // qDebug("QScriptEngine::heuristicSetGlyphAttributes, num_glyphs=%d", item->num_glyphs); 238 HB_GlyphAttributes *attributes = item->attributes; 239 unsigned short *logClusters = item->log_clusters; 240 241 hb_uint32 glyph_pos = 0; 242 hb_uint32 i; 243 for (i = 0; i < length; i++) { 244 if (HB_IsHighSurrogate(uc[i]) && i < length - 1 245 && HB_IsLowSurrogate(uc[i + 1])) { 246 logClusters[i] = glyph_pos; 247 logClusters[++i] = glyph_pos; 248 } else { 249 logClusters[i] = glyph_pos; 250 } 251 ++glyph_pos; 252 } 253 assert(glyph_pos == item->num_glyphs); 254 255 // first char in a run is never (treated as) a mark 256 int cStart = 0; 257 const bool symbolFont = item->face->isSymbolFont; 258 attributes[0].mark = false; 259 attributes[0].clusterStart = true; 260 attributes[0].dontPrint = (!symbolFont && uc[0] == 0x00ad) || HB_IsControlChar(uc[0]); 261 262 int pos = 0; 263 HB_CharCategory lastCat; 264 int dummy; 265 HB_GetUnicodeCharProperties(uc[0], &lastCat, &dummy); 266 for (i = 1; i < length; ++i) { 267 if (logClusters[i] == pos) 268 // same glyph 269 continue; 270 ++pos; 271 while (pos < logClusters[i]) { 272 attributes[pos] = attributes[pos-1]; 273 ++pos; 274 } 275 // hide soft-hyphens by default 276 if ((!symbolFont && uc[i] == 0x00ad) || HB_IsControlChar(uc[i])) 277 attributes[pos].dontPrint = true; 278 HB_CharCategory cat; 279 int cmb; 280 HB_GetUnicodeCharProperties(uc[i], &cat, &cmb); 281 if (cat != HB_Mark_NonSpacing) { 282 attributes[pos].mark = false; 283 attributes[pos].clusterStart = true; 284 attributes[pos].combiningClass = 0; 285 cStart = logClusters[i]; 286 } else { 287 if (cmb == 0) { 288 // Fix 0 combining classes 289 if ((uc[pos] & 0xff00) == 0x0e00) { 290 // thai or lao 291 if (uc[pos] == 0xe31 || 292 uc[pos] == 0xe34 || 293 uc[pos] == 0xe35 || 294 uc[pos] == 0xe36 || 295 uc[pos] == 0xe37 || 296 uc[pos] == 0xe47 || 297 uc[pos] == 0xe4c || 298 uc[pos] == 0xe4d || 299 uc[pos] == 0xe4e) { 300 cmb = HB_Combining_AboveRight; 301 } else if (uc[pos] == 0xeb1 || 302 uc[pos] == 0xeb4 || 303 uc[pos] == 0xeb5 || 304 uc[pos] == 0xeb6 || 305 uc[pos] == 0xeb7 || 306 uc[pos] == 0xebb || 307 uc[pos] == 0xecc || 308 uc[pos] == 0xecd) { 309 cmb = HB_Combining_Above; 310 } else if (uc[pos] == 0xebc) { 311 cmb = HB_Combining_Below; 312 } 313 } 314 } 315 316 attributes[pos].mark = true; 317 attributes[pos].clusterStart = false; 318 attributes[pos].combiningClass = cmb; 319 logClusters[i] = cStart; 320 } 321 // one gets an inter character justification point if the current char is not a non spacing mark. 322 // as then the current char belongs to the last one and one gets a space justification point 323 // after the space char. 324 if (lastCat == HB_Separator_Space) 325 attributes[pos-1].justification = HB_Space; 326 else if (cat != HB_Mark_NonSpacing) 327 attributes[pos-1].justification = HB_Character; 328 else 329 attributes[pos-1].justification = HB_NoJustification; 330 331 lastCat = cat; 332 } 333 pos = logClusters[length-1]; 334 if (lastCat == HB_Separator_Space) 335 attributes[pos].justification = HB_Space; 336 else 337 attributes[pos].justification = HB_Character; 338 } 339 340 #ifndef NO_OPENTYPE 341 static const HB_OpenTypeFeature basic_features[] = { 342 { HB_MAKE_TAG('c', 'c', 'm', 'p'), CcmpProperty }, 343 { HB_MAKE_TAG('l', 'i', 'g', 'a'), CcmpProperty }, 344 { HB_MAKE_TAG('c', 'l', 'i', 'g'), CcmpProperty }, 345 {0, 0} 346 }; 347 #endif 348 349 HB_Bool HB_ConvertStringToGlyphIndices(HB_ShaperItem *shaper_item) 350 { 351 if (shaper_item->glyphIndicesPresent) { 352 shaper_item->num_glyphs = shaper_item->initialGlyphCount; 353 shaper_item->glyphIndicesPresent = false; 354 return true; 355 } 356 return shaper_item->font->klass 357 ->convertStringToGlyphIndices(shaper_item->font, 358 shaper_item->string + shaper_item->item.pos, shaper_item->item.length, 359 shaper_item->glyphs, &shaper_item->num_glyphs, 360 shaper_item->item.bidiLevel % 2); 361 } 362 363 HB_Bool HB_BasicShape(HB_ShaperItem *shaper_item) 364 { 365 #ifndef NO_OPENTYPE 366 const int availableGlyphs = shaper_item->num_glyphs; 367 #endif 368 369 if (!HB_ConvertStringToGlyphIndices(shaper_item)) 370 return false; 371 372 HB_HeuristicSetGlyphAttributes(shaper_item); 373 374 #ifndef NO_OPENTYPE 375 if (HB_SelectScript(shaper_item, basic_features)) { 376 HB_OpenTypeShape(shaper_item, /*properties*/0); 377 return HB_OpenTypePosition(shaper_item, availableGlyphs, /*doLogClusters*/true); 378 } 379 #endif 380 381 HB_HeuristicPosition(shaper_item); 382 return true; 383 } 384 385 const HB_ScriptEngine HB_ScriptEngines[] = { 386 // Common 387 { HB_BasicShape}, 388 // Greek 389 { HB_GreekShape}, 390 // Cyrillic 391 { HB_BasicShape}, 392 // Armenian 393 { HB_BasicShape}, 394 // Hebrew 395 { HB_HebrewShape}, 396 // Arabic 397 { HB_ArabicShape}, 398 // Syriac 399 { HB_ArabicShape}, 400 // Thaana 401 { HB_BasicShape}, 402 // Devanagari 403 { HB_IndicShape}, 404 // Bengali 405 { HB_IndicShape}, 406 // Gurmukhi 407 { HB_IndicShape}, 408 // Gujarati 409 { HB_IndicShape}, 410 // Oriya 411 { HB_IndicShape}, 412 // Tamil 413 { HB_IndicShape}, 414 // Telugu 415 { HB_IndicShape}, 416 // Kannada 417 { HB_IndicShape}, 418 // Malayalam 419 { HB_IndicShape}, 420 // Sinhala 421 { HB_IndicShape}, 422 // Thai 423 { HB_BasicShape}, 424 // Lao 425 { HB_BasicShape}, 426 // Tibetan 427 { HB_TibetanShape}, 428 // Myanmar 429 { HB_MyanmarShape}, 430 // Georgian 431 { HB_BasicShape}, 432 // Hangul 433 { HB_HangulShape}, 434 // Ogham 435 { HB_BasicShape}, 436 // Runic 437 { HB_BasicShape}, 438 // Khmer 439 { HB_KhmerShape}, 440 // N'Ko 441 { HB_ArabicShape} 442 }; 443 444 445 static inline char *tag_to_string(HB_UInt tag) 446 { 447 static char string[5]; 448 string[0] = (tag >> 24)&0xff; 449 string[1] = (tag >> 16)&0xff; 450 string[2] = (tag >> 8)&0xff; 451 string[3] = tag&0xff; 452 string[4] = 0; 453 return string; 454 } 455 456 #ifdef OT_DEBUG 457 static void dump_string(HB_Buffer buffer) 458 { 459 for (uint i = 0; i < buffer->in_length; ++i) { 460 qDebug(" %x: cluster=%d", buffer->in_string[i].gindex, buffer->in_string[i].cluster); 461 } 462 } 463 #define DEBUG printf 464 #else 465 #define DEBUG if (1) ; else printf 466 #endif 467 468 #if 0 469 #define DefaultLangSys 0xffff 470 #define DefaultScript HB_MAKE_TAG('D', 'F', 'L', 'T') 471 #endif 472 473 enum { 474 RequiresGsub = 1, 475 RequiresGpos = 2 476 }; 477 478 struct OTScripts { 479 unsigned int tag; 480 int flags; 481 }; 482 static const OTScripts ot_scripts [] = { 483 // Common 484 { HB_MAKE_TAG('l', 'a', 't', 'n'), 0 }, 485 // Greek 486 { HB_MAKE_TAG('g', 'r', 'e', 'k'), 0 }, 487 // Cyrillic 488 { HB_MAKE_TAG('c', 'y', 'r', 'l'), 0 }, 489 // Armenian 490 { HB_MAKE_TAG('a', 'r', 'm', 'n'), 0 }, 491 // Hebrew 492 { HB_MAKE_TAG('h', 'e', 'b', 'r'), 1 }, 493 // Arabic 494 { HB_MAKE_TAG('a', 'r', 'a', 'b'), 1 }, 495 // Syriac 496 { HB_MAKE_TAG('s', 'y', 'r', 'c'), 1 }, 497 // Thaana 498 { HB_MAKE_TAG('t', 'h', 'a', 'a'), 1 }, 499 // Devanagari 500 { HB_MAKE_TAG('d', 'e', 'v', 'a'), 1 }, 501 // Bengali 502 { HB_MAKE_TAG('b', 'e', 'n', 'g'), 1 }, 503 // Gurmukhi 504 { HB_MAKE_TAG('g', 'u', 'r', 'u'), 1 }, 505 // Gujarati 506 { HB_MAKE_TAG('g', 'u', 'j', 'r'), 1 }, 507 // Oriya 508 { HB_MAKE_TAG('o', 'r', 'y', 'a'), 1 }, 509 // Tamil 510 { HB_MAKE_TAG('t', 'a', 'm', 'l'), 1 }, 511 // Telugu 512 { HB_MAKE_TAG('t', 'e', 'l', 'u'), 1 }, 513 // Kannada 514 { HB_MAKE_TAG('k', 'n', 'd', 'a'), 1 }, 515 // Malayalam 516 { HB_MAKE_TAG('m', 'l', 'y', 'm'), 1 }, 517 // Sinhala 518 { HB_MAKE_TAG('s', 'i', 'n', 'h'), 1 }, 519 // Thai 520 { HB_MAKE_TAG('t', 'h', 'a', 'i'), 1 }, 521 // Lao 522 { HB_MAKE_TAG('l', 'a', 'o', ' '), 1 }, 523 // Tibetan 524 { HB_MAKE_TAG('t', 'i', 'b', 't'), 1 }, 525 // Myanmar 526 { HB_MAKE_TAG('m', 'y', 'm', 'r'), 1 }, 527 // Georgian 528 { HB_MAKE_TAG('g', 'e', 'o', 'r'), 0 }, 529 // Hangul 530 { HB_MAKE_TAG('h', 'a', 'n', 'g'), 1 }, 531 // Ogham 532 { HB_MAKE_TAG('o', 'g', 'a', 'm'), 0 }, 533 // Runic 534 { HB_MAKE_TAG('r', 'u', 'n', 'r'), 0 }, 535 // Khmer 536 { HB_MAKE_TAG('k', 'h', 'm', 'r'), 1 }, 537 // N'Ko 538 { HB_MAKE_TAG('n', 'k', 'o', ' '), 1 } 539 }; 540 enum { NumOTScripts = sizeof(ot_scripts)/sizeof(OTScripts) }; 541 542 static HB_Bool checkScript(HB_Face face, int script) 543 { 544 assert(script < HB_ScriptCount); 545 546 if (!face->gsub && !face->gpos) 547 return false; 548 549 unsigned int tag = ot_scripts[script].tag; 550 int requirements = ot_scripts[script].flags; 551 552 if (requirements & RequiresGsub) { 553 if (!face->gsub) 554 return false; 555 556 HB_UShort script_index; 557 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index); 558 if (error) { 559 DEBUG("could not select script %d in GSub table: %d", (int)script, error); 560 error = HB_GSUB_Select_Script(face->gsub, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index); 561 if (error) 562 return false; 563 } 564 } 565 566 if (requirements & RequiresGpos) { 567 if (!face->gpos) 568 return false; 569 570 HB_UShort script_index; 571 HB_Error error = HB_GPOS_Select_Script(face->gpos, script, &script_index); 572 if (error) { 573 DEBUG("could not select script in gpos table: %d", error); 574 error = HB_GPOS_Select_Script(face->gpos, HB_MAKE_TAG('D', 'F', 'L', 'T'), &script_index); 575 if (error) 576 return false; 577 } 578 579 } 580 return true; 581 } 582 583 static HB_Stream getTableStream(void *font, HB_GetFontTableFunc tableFunc, HB_Tag tag) 584 { 585 HB_Error error; 586 HB_UInt length = 0; 587 HB_Stream stream = 0; 588 589 if (!font) 590 return 0; 591 592 error = tableFunc(font, tag, 0, &length); 593 if (error) 594 return 0; 595 stream = (HB_Stream)malloc(sizeof(HB_StreamRec)); 596 if (!stream) 597 return 0; 598 stream->base = (HB_Byte*)malloc(length); 599 if (!stream->base) { 600 free(stream); 601 return 0; 602 } 603 error = tableFunc(font, tag, stream->base, &length); 604 if (error) { 605 _hb_close_stream(stream); 606 return 0; 607 } 608 stream->size = length; 609 stream->pos = 0; 610 stream->cursor = NULL; 611 return stream; 612 } 613 614 HB_Face HB_NewFace(void *font, HB_GetFontTableFunc tableFunc) 615 { 616 HB_Face face = (HB_Face )malloc(sizeof(HB_FaceRec)); 617 if (!face) 618 return 0; 619 620 face->isSymbolFont = false; 621 face->gdef = 0; 622 face->gpos = 0; 623 face->gsub = 0; 624 face->current_script = HB_ScriptCount; 625 face->current_flags = HB_ShaperFlag_Default; 626 face->has_opentype_kerning = false; 627 face->tmpAttributes = 0; 628 face->tmpLogClusters = 0; 629 face->glyphs_substituted = false; 630 face->buffer = 0; 631 632 HB_Error error = HB_Err_Ok; 633 HB_Stream stream; 634 HB_Stream gdefStream; 635 636 gdefStream = getTableStream(font, tableFunc, TTAG_GDEF); 637 error = HB_Err_Not_Covered; 638 if (!gdefStream || (error = HB_Load_GDEF_Table(gdefStream, &face->gdef))) { 639 //DEBUG("error loading gdef table: %d", error); 640 face->gdef = 0; 641 } 642 643 //DEBUG() << "trying to load gsub table"; 644 stream = getTableStream(font, tableFunc, TTAG_GSUB); 645 error = HB_Err_Not_Covered; 646 if (!stream || (error = HB_Load_GSUB_Table(stream, &face->gsub, face->gdef, gdefStream))) { 647 face->gsub = 0; 648 if (error != HB_Err_Not_Covered) { 649 //DEBUG("error loading gsub table: %d", error); 650 } else { 651 //DEBUG("face doesn't have a gsub table"); 652 } 653 } 654 _hb_close_stream(stream); 655 656 stream = getTableStream(font, tableFunc, TTAG_GPOS); 657 error = HB_Err_Not_Covered; 658 if (!stream || (error = HB_Load_GPOS_Table(stream, &face->gpos, face->gdef, gdefStream))) { 659 face->gpos = 0; 660 DEBUG("error loading gpos table: %d", error); 661 } 662 _hb_close_stream(stream); 663 664 _hb_close_stream(gdefStream); 665 666 for (unsigned int i = 0; i < HB_ScriptCount; ++i) 667 face->supported_scripts[i] = checkScript(face, i); 668 669 if (HB_Buffer_new(&face->buffer) != HB_Err_Ok) { 670 HB_FreeFace(face); 671 return 0; 672 } 673 674 return face; 675 } 676 677 void HB_FreeFace(HB_Face face) 678 { 679 if (!face) 680 return; 681 if (face->gpos) 682 HB_Done_GPOS_Table(face->gpos); 683 if (face->gsub) 684 HB_Done_GSUB_Table(face->gsub); 685 if (face->gdef) 686 HB_Done_GDEF_Table(face->gdef); 687 if (face->buffer) 688 HB_Buffer_free(face->buffer); 689 if (face->tmpAttributes) 690 free(face->tmpAttributes); 691 if (face->tmpLogClusters) 692 free(face->tmpLogClusters); 693 free(face); 694 } 695 696 HB_Bool HB_SelectScript(HB_ShaperItem *shaper_item, const HB_OpenTypeFeature *features) 697 { 698 HB_Script script = shaper_item->item.script; 699 700 if (!shaper_item->face->supported_scripts[script]) 701 return false; 702 703 HB_Face face = shaper_item->face; 704 if (face->current_script == script && face->current_flags == shaper_item->shaperFlags) 705 return true; 706 707 face->current_script = script; 708 face->current_flags = shaper_item->shaperFlags; 709 710 assert(script < HB_ScriptCount); 711 // find script in our list of supported scripts. 712 unsigned int tag = ot_scripts[script].tag; 713 714 if (face->gsub && features) { 715 #ifdef OT_DEBUG 716 { 717 HB_FeatureList featurelist = face->gsub->FeatureList; 718 int numfeatures = featurelist.FeatureCount; 719 DEBUG("gsub table has %d features", numfeatures); 720 for (int i = 0; i < numfeatures; i++) { 721 HB_FeatureRecord *r = featurelist.FeatureRecord + i; 722 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag)); 723 } 724 } 725 #endif 726 HB_GSUB_Clear_Features(face->gsub); 727 HB_UShort script_index; 728 HB_Error error = HB_GSUB_Select_Script(face->gsub, tag, &script_index); 729 if (!error) { 730 DEBUG("script %s has script index %d", tag_to_string(script), script_index); 731 while (features->tag) { 732 HB_UShort feature_index; 733 error = HB_GSUB_Select_Feature(face->gsub, features->tag, script_index, 0xffff, &feature_index); 734 if (!error) { 735 DEBUG(" adding feature %s", tag_to_string(features->tag)); 736 HB_GSUB_Add_Feature(face->gsub, feature_index, features->property); 737 } 738 ++features; 739 } 740 } 741 } 742 743 // reset 744 face->has_opentype_kerning = false; 745 746 if (face->gpos) { 747 HB_GPOS_Clear_Features(face->gpos); 748 HB_UShort script_index; 749 HB_Error error = HB_GPOS_Select_Script(face->gpos, tag, &script_index); 750 if (!error) { 751 #ifdef OT_DEBUG 752 { 753 HB_FeatureList featurelist = face->gpos->FeatureList; 754 int numfeatures = featurelist.FeatureCount; 755 DEBUG("gpos table has %d features", numfeatures); 756 for(int i = 0; i < numfeatures; i++) { 757 HB_FeatureRecord *r = featurelist.FeatureRecord + i; 758 HB_UShort feature_index; 759 HB_GPOS_Select_Feature(face->gpos, r->FeatureTag, script_index, 0xffff, &feature_index); 760 DEBUG(" feature '%s'", tag_to_string(r->FeatureTag)); 761 } 762 } 763 #endif 764 HB_UInt *feature_tag_list_buffer; 765 error = HB_GPOS_Query_Features(face->gpos, script_index, 0xffff, &feature_tag_list_buffer); 766 if (!error) { 767 HB_UInt *feature_tag_list = feature_tag_list_buffer; 768 while (*feature_tag_list) { 769 HB_UShort feature_index; 770 if (*feature_tag_list == HB_MAKE_TAG('k', 'e', 'r', 'n')) { 771 if (face->current_flags & HB_ShaperFlag_NoKerning) { 772 ++feature_tag_list; 773 continue; 774 } 775 face->has_opentype_kerning = true; 776 } 777 error = HB_GPOS_Select_Feature(face->gpos, *feature_tag_list, script_index, 0xffff, &feature_index); 778 if (!error) 779 HB_GPOS_Add_Feature(face->gpos, feature_index, PositioningProperties); 780 ++feature_tag_list; 781 } 782 FREE(feature_tag_list_buffer); 783 } 784 } 785 } 786 787 return true; 788 } 789 790 HB_Bool HB_OpenTypeShape(HB_ShaperItem *item, const hb_uint32 *properties) 791 { 792 HB_GlyphAttributes *tmpAttributes; 793 unsigned int *tmpLogClusters; 794 795 HB_Face face = item->face; 796 797 face->length = item->num_glyphs; 798 799 HB_Buffer_clear(face->buffer); 800 801 tmpAttributes = (HB_GlyphAttributes *) realloc(face->tmpAttributes, face->length*sizeof(HB_GlyphAttributes)); 802 if (!tmpAttributes) 803 return false; 804 face->tmpAttributes = tmpAttributes; 805 806 tmpLogClusters = (unsigned int *) realloc(face->tmpLogClusters, face->length*sizeof(unsigned int)); 807 if (!tmpLogClusters) 808 return false; 809 face->tmpLogClusters = tmpLogClusters; 810 811 for (int i = 0; i < face->length; ++i) { 812 HB_Buffer_add_glyph(face->buffer, item->glyphs[i], properties ? properties[i] : 0, i); 813 face->tmpAttributes[i] = item->attributes[i]; 814 face->tmpLogClusters[i] = item->log_clusters[i]; 815 } 816 817 #ifdef OT_DEBUG 818 DEBUG("-----------------------------------------"); 819 // DEBUG("log clusters before shaping:"); 820 // for (int j = 0; j < length; j++) 821 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]); 822 DEBUG("original glyphs: %p", item->glyphs); 823 for (int i = 0; i < length; ++i) 824 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex); 825 // dump_string(hb_buffer); 826 #endif 827 828 face->glyphs_substituted = false; 829 if (face->gsub) { 830 unsigned int error = HB_GSUB_Apply_String(face->gsub, face->buffer); 831 if (error && error != HB_Err_Not_Covered) 832 return false; 833 face->glyphs_substituted = (error != HB_Err_Not_Covered); 834 } 835 836 #ifdef OT_DEBUG 837 // DEBUG("log clusters before shaping:"); 838 // for (int j = 0; j < length; j++) 839 // DEBUG(" log[%d] = %d", j, item->log_clusters[j]); 840 DEBUG("shaped glyphs:"); 841 for (int i = 0; i < length; ++i) 842 DEBUG(" glyph=%4x", hb_buffer->in_string[i].gindex); 843 DEBUG("-----------------------------------------"); 844 // dump_string(hb_buffer); 845 #endif 846 847 return true; 848 } 849 850 HB_Bool HB_OpenTypePosition(HB_ShaperItem *item, int availableGlyphs, HB_Bool doLogClusters) 851 { 852 HB_Face face = item->face; 853 854 bool glyphs_positioned = false; 855 if (face->gpos) { 856 if (face->buffer->positions) 857 memset(face->buffer->positions, 0, face->buffer->in_length*sizeof(HB_PositionRec)); 858 // #### check that passing "false,false" is correct 859 glyphs_positioned = HB_GPOS_Apply_String(item->font, face->gpos, face->current_flags, face->buffer, false, false) != HB_Err_Not_Covered; 860 } 861 862 if (!face->glyphs_substituted && !glyphs_positioned) { 863 HB_GetGlyphAdvances(item); 864 return true; // nothing to do for us 865 } 866 867 // make sure we have enough space to write everything back 868 if (availableGlyphs < (int)face->buffer->in_length) { 869 item->num_glyphs = face->buffer->in_length; 870 return false; 871 } 872 873 HB_Glyph *glyphs = item->glyphs; 874 HB_GlyphAttributes *attributes = item->attributes; 875 876 for (unsigned int i = 0; i < face->buffer->in_length; ++i) { 877 glyphs[i] = face->buffer->in_string[i].gindex; 878 attributes[i] = face->tmpAttributes[face->buffer->in_string[i].cluster]; 879 if (i && face->buffer->in_string[i].cluster == face->buffer->in_string[i-1].cluster) 880 attributes[i].clusterStart = false; 881 } 882 item->num_glyphs = face->buffer->in_length; 883 884 if (doLogClusters && face->glyphs_substituted) { 885 // we can't do this for indic, as we pass the stuf in syllables and it's easier to do it in the shaper. 886 unsigned short *logClusters = item->log_clusters; 887 int clusterStart = 0; 888 int oldCi = 0; 889 // #### the reconstruction of the logclusters currently does not work if the original string 890 // contains surrogate pairs 891 for (unsigned int i = 0; i < face->buffer->in_length; ++i) { 892 int ci = face->buffer->in_string[i].cluster; 893 // DEBUG(" ci[%d] = %d mark=%d, cmb=%d, cs=%d", 894 // i, ci, glyphAttributes[i].mark, glyphAttributes[i].combiningClass, glyphAttributes[i].clusterStart); 895 if (!attributes[i].mark && attributes[i].clusterStart && ci != oldCi) { 896 for (int j = oldCi; j < ci; j++) 897 logClusters[j] = clusterStart; 898 clusterStart = i; 899 oldCi = ci; 900 } 901 } 902 for (int j = oldCi; j < face->length; j++) 903 logClusters[j] = clusterStart; 904 } 905 906 // calulate the advances for the shaped glyphs 907 // DEBUG("unpositioned: "); 908 909 // positioning code: 910 if (glyphs_positioned) { 911 HB_GetGlyphAdvances(item); 912 HB_Position positions = face->buffer->positions; 913 HB_Fixed *advances = item->advances; 914 915 // DEBUG("positioned glyphs:"); 916 for (unsigned int i = 0; i < face->buffer->in_length; i++) { 917 // DEBUG(" %d:\t orig advance: (%d/%d)\tadv=(%d/%d)\tpos=(%d/%d)\tback=%d\tnew_advance=%d", i, 918 // glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(), 919 // (int)(positions[i].x_advance >> 6), (int)(positions[i].y_advance >> 6), 920 // (int)(positions[i].x_pos >> 6), (int)(positions[i].y_pos >> 6), 921 // positions[i].back, positions[i].new_advance); 922 923 HB_Fixed adjustment = positions[i].x_advance; 924 925 if (!(face->current_flags & HB_ShaperFlag_UseDesignMetrics)) 926 adjustment = HB_FIXED_ROUND(adjustment); 927 928 if (positions[i].new_advance) { 929 ; //advances[i] = adjustment; 930 } else { 931 advances[i] += adjustment; 932 } 933 934 int back = 0; 935 HB_FixedPoint *offsets = item->offsets; 936 offsets[i].x = positions[i].x_pos; 937 offsets[i].y = positions[i].y_pos; 938 while (positions[i - back].back) { 939 back += positions[i - back].back; 940 offsets[i].x += positions[i - back].x_pos; 941 offsets[i].y += positions[i - back].y_pos; 942 } 943 offsets[i].y = -offsets[i].y; 944 945 if (item->item.bidiLevel % 2) { 946 // ### may need to go back multiple glyphs like in ltr 947 back = positions[i].back; 948 while (back--) 949 offsets[i].x -= advances[i-back]; 950 } else { 951 back = 0; 952 while (positions[i - back].back) { 953 back += positions[i - back].back; 954 offsets[i].x -= advances[i-back]; 955 } 956 } 957 // DEBUG(" ->\tadv=%d\tpos=(%d/%d)", 958 // glyphs[i].advance.x.toInt(), glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt()); 959 } 960 item->kerning_applied = face->has_opentype_kerning; 961 } else { 962 HB_HeuristicPosition(item); 963 } 964 965 #ifdef OT_DEBUG 966 if (doLogClusters) { 967 DEBUG("log clusters after shaping:"); 968 for (int j = 0; j < length; j++) 969 DEBUG(" log[%d] = %d", j, item->log_clusters[j]); 970 } 971 DEBUG("final glyphs:"); 972 for (int i = 0; i < (int)hb_buffer->in_length; ++i) 973 DEBUG(" glyph=%4x char_index=%d mark: %d cmp: %d, clusterStart: %d advance=%d/%d offset=%d/%d", 974 glyphs[i].glyph, hb_buffer->in_string[i].cluster, glyphs[i].attributes.mark, 975 glyphs[i].attributes.combiningClass, glyphs[i].attributes.clusterStart, 976 glyphs[i].advance.x.toInt(), glyphs[i].advance.y.toInt(), 977 glyphs[i].offset.x.toInt(), glyphs[i].offset.y.toInt()); 978 DEBUG("-----------------------------------------"); 979 #endif 980 return true; 981 } 982 983 HB_Bool HB_ShapeItem(HB_ShaperItem *shaper_item) 984 { 985 HB_Bool result = false; 986 if (shaper_item->num_glyphs < shaper_item->item.length) { 987 shaper_item->num_glyphs = shaper_item->item.length; 988 return false; 989 } 990 assert(shaper_item->item.script < HB_ScriptCount); 991 result = HB_ScriptEngines[shaper_item->item.script].shape(shaper_item); 992 shaper_item->glyphIndicesPresent = false; 993 return result; 994 } 995