2d.text.measure.index-from-offset.tentative.html (154691B)
1 <!DOCTYPE html> 2 <!-- DO NOT EDIT! This test has been generated by /html/canvas/tools/gentest.py. --> 3 <meta charset="UTF-8"> 4 <title>Canvas test: 2d.text.measure.index-from-offset.tentative</title> 5 <script src="/resources/testharness.js"></script> 6 <script src="/resources/testharnessreport.js"></script> 7 <script src="/html/canvas/resources/canvas-tests.js"></script> 8 <link rel="stylesheet" href="/html/canvas/resources/canvas-tests.css"> 9 10 <h1>2d.text.measure.index-from-offset.tentative</h1> 11 12 <script> 13 14 test(t => { 15 const canvas = document.createElement('canvas'); 16 canvas.width = 100; 17 canvas.height = 50; 18 const ctx = canvas.getContext('2d'); 19 20 function alignOffset(offset, width) { 21 if ('left' == 'center') { 22 offset -= width / 2; 23 } else if ('left' == 'right' || 24 ('ltr' == 'ltr' && 'left' == 'end') || 25 ('ltr' == 'rtl' && 'left' == 'start')) { 26 offset -= width; 27 } 28 return offset; 29 } 30 31 32 function placeAndMeasureTextInDOM(text, text_width, point) { 33 const el = document.createElement("p"); 34 el.innerHTML = text; 35 el.style.font = '50px sans-serif'; 36 el.style.direction = 'ltr'; 37 el.style.letterSpacing = '0px'; 38 // Put the text top left to make offsets simpler. 39 el.style.padding = '0px'; 40 el.style.margin = '0px'; 41 el.style.position = 'absolute'; 42 el.style.x = '0px'; 43 el.style.y = '0px'; 44 document.body.appendChild(el); 45 text_bound = el.getBoundingClientRect(); 46 text_x = text_bound.x; 47 text_y = text_bound.y + text_bound.height / 2; 48 49 // Offset to the requested point determined by textAlign and direction. 50 let text_align_dx = 0; 51 if ('left' == 'center') { 52 text_align_dx = text_width / 2; 53 } else if ('left' == 'right' || 54 ('ltr' == 'ltr' && 'left' == 'end') || 55 ('ltr' == 'rtl' && 'left' == 'start')) { 56 text_align_dx = text_width; 57 } 58 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 59 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 60 61 document.body.removeChild(el); 62 63 return position.offset; 64 } 65 66 ctx.font = '50px sans-serif'; 67 ctx.direction = 'ltr'; 68 ctx.textAlign = 'left'; 69 ctx.letterSpacing = '0px'; 70 71 const kTexts = [ 72 'UNAVAILABLE', 73 'ππΆπ', 74 'οΌοΌγοΌοΌ', 75 'οΌοΌγγοΌοΌ', 76 'γγοΌοΌγγ', 77 '--abcd__' 78 ] 79 80 for (text of kTexts) { 81 const tm = ctx.measureText(text); 82 text_width = tm.width; 83 step = 30; 84 if ('0px' == '10px') { 85 step = 40; 86 } 87 88 offset = step; 89 adjusted_offset = alignOffset(offset, text_width); 90 tm_position = tm.getIndexFromOffset(adjusted_offset); 91 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 92 assert_equals(tm_position, 93 doc_position, 94 "for " + text + " offset " + offset); 95 96 offset = text_width / 2 - 10; 97 adjusted_offset = alignOffset(offset, text_width); 98 tm_position = tm.getIndexFromOffset(adjusted_offset); 99 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 100 assert_equals(tm_position, 101 doc_position, 102 "for " + text + " offset " + offset); 103 104 offset = text_width / 2 + 10; 105 adjusted_offset = alignOffset(offset, text_width); 106 tm_position = tm.getIndexFromOffset(adjusted_offset); 107 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 108 assert_equals(tm_position, 109 doc_position, 110 "for " + text + " offset " + offset); 111 112 offset = text_width - step; 113 adjusted_offset = alignOffset(offset, text_width); 114 tm_position = tm.getIndexFromOffset(adjusted_offset); 115 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 116 assert_equals(tm_position, 117 doc_position, 118 "for " + text + " offset " + offset); 119 } 120 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 0px letter spacing and no-directional-override."); 121 122 test(t => { 123 const canvas = document.createElement('canvas'); 124 canvas.width = 100; 125 canvas.height = 50; 126 const ctx = canvas.getContext('2d'); 127 128 function alignOffset(offset, width) { 129 if ('left' == 'center') { 130 offset -= width / 2; 131 } else if ('left' == 'right' || 132 ('rtl' == 'ltr' && 'left' == 'end') || 133 ('rtl' == 'rtl' && 'left' == 'start')) { 134 offset -= width; 135 } 136 return offset; 137 } 138 139 140 function placeAndMeasureTextInDOM(text, text_width, point) { 141 const el = document.createElement("p"); 142 el.innerHTML = text; 143 el.style.font = '50px sans-serif'; 144 el.style.direction = 'rtl'; 145 el.style.letterSpacing = '0px'; 146 // Put the text top left to make offsets simpler. 147 el.style.padding = '0px'; 148 el.style.margin = '0px'; 149 el.style.position = 'absolute'; 150 el.style.x = '0px'; 151 el.style.y = '0px'; 152 document.body.appendChild(el); 153 text_bound = el.getBoundingClientRect(); 154 text_x = text_bound.x; 155 text_y = text_bound.y + text_bound.height / 2; 156 157 // Offset to the requested point determined by textAlign and direction. 158 let text_align_dx = 0; 159 if ('left' == 'center') { 160 text_align_dx = text_width / 2; 161 } else if ('left' == 'right' || 162 ('rtl' == 'ltr' && 'left' == 'end') || 163 ('rtl' == 'rtl' && 'left' == 'start')) { 164 text_align_dx = text_width; 165 } 166 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 167 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 168 169 document.body.removeChild(el); 170 171 return position.offset; 172 } 173 174 ctx.font = '50px sans-serif'; 175 ctx.direction = 'rtl'; 176 ctx.textAlign = 'left'; 177 ctx.letterSpacing = '0px'; 178 179 const kTexts = [ 180 'UNAVAILABLE', 181 'ππΆπ', 182 'οΌοΌγοΌοΌ', 183 'οΌοΌγγοΌοΌ', 184 'γγοΌοΌγγ', 185 '--abcd__' 186 ] 187 188 for (text of kTexts) { 189 const tm = ctx.measureText(text); 190 text_width = tm.width; 191 step = 30; 192 if ('0px' == '10px') { 193 step = 40; 194 } 195 196 offset = step; 197 adjusted_offset = alignOffset(offset, text_width); 198 tm_position = tm.getIndexFromOffset(adjusted_offset); 199 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 200 assert_equals(tm_position, 201 doc_position, 202 "for " + text + " offset " + offset); 203 204 offset = text_width / 2 - 10; 205 adjusted_offset = alignOffset(offset, text_width); 206 tm_position = tm.getIndexFromOffset(adjusted_offset); 207 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 208 assert_equals(tm_position, 209 doc_position, 210 "for " + text + " offset " + offset); 211 212 offset = text_width / 2 + 10; 213 adjusted_offset = alignOffset(offset, text_width); 214 tm_position = tm.getIndexFromOffset(adjusted_offset); 215 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 216 assert_equals(tm_position, 217 doc_position, 218 "for " + text + " offset " + offset); 219 220 offset = text_width - step; 221 adjusted_offset = alignOffset(offset, text_width); 222 tm_position = tm.getIndexFromOffset(adjusted_offset); 223 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 224 assert_equals(tm_position, 225 doc_position, 226 "for " + text + " offset " + offset); 227 } 228 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 0px letter spacing and no-directional-override."); 229 230 test(t => { 231 const canvas = document.createElement('canvas'); 232 canvas.width = 100; 233 canvas.height = 50; 234 const ctx = canvas.getContext('2d'); 235 236 function alignOffset(offset, width) { 237 if ('center' == 'center') { 238 offset -= width / 2; 239 } else if ('center' == 'right' || 240 ('ltr' == 'ltr' && 'center' == 'end') || 241 ('ltr' == 'rtl' && 'center' == 'start')) { 242 offset -= width; 243 } 244 return offset; 245 } 246 247 248 function placeAndMeasureTextInDOM(text, text_width, point) { 249 const el = document.createElement("p"); 250 el.innerHTML = text; 251 el.style.font = '50px sans-serif'; 252 el.style.direction = 'ltr'; 253 el.style.letterSpacing = '0px'; 254 // Put the text top left to make offsets simpler. 255 el.style.padding = '0px'; 256 el.style.margin = '0px'; 257 el.style.position = 'absolute'; 258 el.style.x = '0px'; 259 el.style.y = '0px'; 260 document.body.appendChild(el); 261 text_bound = el.getBoundingClientRect(); 262 text_x = text_bound.x; 263 text_y = text_bound.y + text_bound.height / 2; 264 265 // Offset to the requested point determined by textAlign and direction. 266 let text_align_dx = 0; 267 if ('center' == 'center') { 268 text_align_dx = text_width / 2; 269 } else if ('center' == 'right' || 270 ('ltr' == 'ltr' && 'center' == 'end') || 271 ('ltr' == 'rtl' && 'center' == 'start')) { 272 text_align_dx = text_width; 273 } 274 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 275 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 276 277 document.body.removeChild(el); 278 279 return position.offset; 280 } 281 282 ctx.font = '50px sans-serif'; 283 ctx.direction = 'ltr'; 284 ctx.textAlign = 'center'; 285 ctx.letterSpacing = '0px'; 286 287 const kTexts = [ 288 'UNAVAILABLE', 289 'ππΆπ', 290 'οΌοΌγοΌοΌ', 291 'οΌοΌγγοΌοΌ', 292 'γγοΌοΌγγ', 293 '--abcd__' 294 ] 295 296 for (text of kTexts) { 297 const tm = ctx.measureText(text); 298 text_width = tm.width; 299 step = 30; 300 if ('0px' == '10px') { 301 step = 40; 302 } 303 304 offset = step; 305 adjusted_offset = alignOffset(offset, text_width); 306 tm_position = tm.getIndexFromOffset(adjusted_offset); 307 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 308 assert_equals(tm_position, 309 doc_position, 310 "for " + text + " offset " + offset); 311 312 offset = text_width / 2 - 10; 313 adjusted_offset = alignOffset(offset, text_width); 314 tm_position = tm.getIndexFromOffset(adjusted_offset); 315 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 316 assert_equals(tm_position, 317 doc_position, 318 "for " + text + " offset " + offset); 319 320 offset = text_width / 2 + 10; 321 adjusted_offset = alignOffset(offset, text_width); 322 tm_position = tm.getIndexFromOffset(adjusted_offset); 323 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 324 assert_equals(tm_position, 325 doc_position, 326 "for " + text + " offset " + offset); 327 328 offset = text_width - step; 329 adjusted_offset = alignOffset(offset, text_width); 330 tm_position = tm.getIndexFromOffset(adjusted_offset); 331 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 332 assert_equals(tm_position, 333 doc_position, 334 "for " + text + " offset " + offset); 335 } 336 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 0px letter spacing and no-directional-override."); 337 338 test(t => { 339 const canvas = document.createElement('canvas'); 340 canvas.width = 100; 341 canvas.height = 50; 342 const ctx = canvas.getContext('2d'); 343 344 function alignOffset(offset, width) { 345 if ('center' == 'center') { 346 offset -= width / 2; 347 } else if ('center' == 'right' || 348 ('rtl' == 'ltr' && 'center' == 'end') || 349 ('rtl' == 'rtl' && 'center' == 'start')) { 350 offset -= width; 351 } 352 return offset; 353 } 354 355 356 function placeAndMeasureTextInDOM(text, text_width, point) { 357 const el = document.createElement("p"); 358 el.innerHTML = text; 359 el.style.font = '50px sans-serif'; 360 el.style.direction = 'rtl'; 361 el.style.letterSpacing = '0px'; 362 // Put the text top left to make offsets simpler. 363 el.style.padding = '0px'; 364 el.style.margin = '0px'; 365 el.style.position = 'absolute'; 366 el.style.x = '0px'; 367 el.style.y = '0px'; 368 document.body.appendChild(el); 369 text_bound = el.getBoundingClientRect(); 370 text_x = text_bound.x; 371 text_y = text_bound.y + text_bound.height / 2; 372 373 // Offset to the requested point determined by textAlign and direction. 374 let text_align_dx = 0; 375 if ('center' == 'center') { 376 text_align_dx = text_width / 2; 377 } else if ('center' == 'right' || 378 ('rtl' == 'ltr' && 'center' == 'end') || 379 ('rtl' == 'rtl' && 'center' == 'start')) { 380 text_align_dx = text_width; 381 } 382 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 383 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 384 385 document.body.removeChild(el); 386 387 return position.offset; 388 } 389 390 ctx.font = '50px sans-serif'; 391 ctx.direction = 'rtl'; 392 ctx.textAlign = 'center'; 393 ctx.letterSpacing = '0px'; 394 395 const kTexts = [ 396 'UNAVAILABLE', 397 'ππΆπ', 398 'οΌοΌγοΌοΌ', 399 'οΌοΌγγοΌοΌ', 400 'γγοΌοΌγγ', 401 '--abcd__' 402 ] 403 404 for (text of kTexts) { 405 const tm = ctx.measureText(text); 406 text_width = tm.width; 407 step = 30; 408 if ('0px' == '10px') { 409 step = 40; 410 } 411 412 offset = step; 413 adjusted_offset = alignOffset(offset, text_width); 414 tm_position = tm.getIndexFromOffset(adjusted_offset); 415 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 416 assert_equals(tm_position, 417 doc_position, 418 "for " + text + " offset " + offset); 419 420 offset = text_width / 2 - 10; 421 adjusted_offset = alignOffset(offset, text_width); 422 tm_position = tm.getIndexFromOffset(adjusted_offset); 423 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 424 assert_equals(tm_position, 425 doc_position, 426 "for " + text + " offset " + offset); 427 428 offset = text_width / 2 + 10; 429 adjusted_offset = alignOffset(offset, text_width); 430 tm_position = tm.getIndexFromOffset(adjusted_offset); 431 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 432 assert_equals(tm_position, 433 doc_position, 434 "for " + text + " offset " + offset); 435 436 offset = text_width - step; 437 adjusted_offset = alignOffset(offset, text_width); 438 tm_position = tm.getIndexFromOffset(adjusted_offset); 439 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 440 assert_equals(tm_position, 441 doc_position, 442 "for " + text + " offset " + offset); 443 } 444 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 0px letter spacing and no-directional-override."); 445 446 test(t => { 447 const canvas = document.createElement('canvas'); 448 canvas.width = 100; 449 canvas.height = 50; 450 const ctx = canvas.getContext('2d'); 451 452 function alignOffset(offset, width) { 453 if ('right' == 'center') { 454 offset -= width / 2; 455 } else if ('right' == 'right' || 456 ('ltr' == 'ltr' && 'right' == 'end') || 457 ('ltr' == 'rtl' && 'right' == 'start')) { 458 offset -= width; 459 } 460 return offset; 461 } 462 463 464 function placeAndMeasureTextInDOM(text, text_width, point) { 465 const el = document.createElement("p"); 466 el.innerHTML = text; 467 el.style.font = '50px sans-serif'; 468 el.style.direction = 'ltr'; 469 el.style.letterSpacing = '0px'; 470 // Put the text top left to make offsets simpler. 471 el.style.padding = '0px'; 472 el.style.margin = '0px'; 473 el.style.position = 'absolute'; 474 el.style.x = '0px'; 475 el.style.y = '0px'; 476 document.body.appendChild(el); 477 text_bound = el.getBoundingClientRect(); 478 text_x = text_bound.x; 479 text_y = text_bound.y + text_bound.height / 2; 480 481 // Offset to the requested point determined by textAlign and direction. 482 let text_align_dx = 0; 483 if ('right' == 'center') { 484 text_align_dx = text_width / 2; 485 } else if ('right' == 'right' || 486 ('ltr' == 'ltr' && 'right' == 'end') || 487 ('ltr' == 'rtl' && 'right' == 'start')) { 488 text_align_dx = text_width; 489 } 490 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 491 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 492 493 document.body.removeChild(el); 494 495 return position.offset; 496 } 497 498 ctx.font = '50px sans-serif'; 499 ctx.direction = 'ltr'; 500 ctx.textAlign = 'right'; 501 ctx.letterSpacing = '0px'; 502 503 const kTexts = [ 504 'UNAVAILABLE', 505 'ππΆπ', 506 'οΌοΌγοΌοΌ', 507 'οΌοΌγγοΌοΌ', 508 'γγοΌοΌγγ', 509 '--abcd__' 510 ] 511 512 for (text of kTexts) { 513 const tm = ctx.measureText(text); 514 text_width = tm.width; 515 step = 30; 516 if ('0px' == '10px') { 517 step = 40; 518 } 519 520 offset = step; 521 adjusted_offset = alignOffset(offset, text_width); 522 tm_position = tm.getIndexFromOffset(adjusted_offset); 523 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 524 assert_equals(tm_position, 525 doc_position, 526 "for " + text + " offset " + offset); 527 528 offset = text_width / 2 - 10; 529 adjusted_offset = alignOffset(offset, text_width); 530 tm_position = tm.getIndexFromOffset(adjusted_offset); 531 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 532 assert_equals(tm_position, 533 doc_position, 534 "for " + text + " offset " + offset); 535 536 offset = text_width / 2 + 10; 537 adjusted_offset = alignOffset(offset, text_width); 538 tm_position = tm.getIndexFromOffset(adjusted_offset); 539 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 540 assert_equals(tm_position, 541 doc_position, 542 "for " + text + " offset " + offset); 543 544 offset = text_width - step; 545 adjusted_offset = alignOffset(offset, text_width); 546 tm_position = tm.getIndexFromOffset(adjusted_offset); 547 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 548 assert_equals(tm_position, 549 doc_position, 550 "for " + text + " offset " + offset); 551 } 552 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 0px letter spacing and no-directional-override."); 553 554 test(t => { 555 const canvas = document.createElement('canvas'); 556 canvas.width = 100; 557 canvas.height = 50; 558 const ctx = canvas.getContext('2d'); 559 560 function alignOffset(offset, width) { 561 if ('right' == 'center') { 562 offset -= width / 2; 563 } else if ('right' == 'right' || 564 ('rtl' == 'ltr' && 'right' == 'end') || 565 ('rtl' == 'rtl' && 'right' == 'start')) { 566 offset -= width; 567 } 568 return offset; 569 } 570 571 572 function placeAndMeasureTextInDOM(text, text_width, point) { 573 const el = document.createElement("p"); 574 el.innerHTML = text; 575 el.style.font = '50px sans-serif'; 576 el.style.direction = 'rtl'; 577 el.style.letterSpacing = '0px'; 578 // Put the text top left to make offsets simpler. 579 el.style.padding = '0px'; 580 el.style.margin = '0px'; 581 el.style.position = 'absolute'; 582 el.style.x = '0px'; 583 el.style.y = '0px'; 584 document.body.appendChild(el); 585 text_bound = el.getBoundingClientRect(); 586 text_x = text_bound.x; 587 text_y = text_bound.y + text_bound.height / 2; 588 589 // Offset to the requested point determined by textAlign and direction. 590 let text_align_dx = 0; 591 if ('right' == 'center') { 592 text_align_dx = text_width / 2; 593 } else if ('right' == 'right' || 594 ('rtl' == 'ltr' && 'right' == 'end') || 595 ('rtl' == 'rtl' && 'right' == 'start')) { 596 text_align_dx = text_width; 597 } 598 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 599 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 600 601 document.body.removeChild(el); 602 603 return position.offset; 604 } 605 606 ctx.font = '50px sans-serif'; 607 ctx.direction = 'rtl'; 608 ctx.textAlign = 'right'; 609 ctx.letterSpacing = '0px'; 610 611 const kTexts = [ 612 'UNAVAILABLE', 613 'ππΆπ', 614 'οΌοΌγοΌοΌ', 615 'οΌοΌγγοΌοΌ', 616 'γγοΌοΌγγ', 617 '--abcd__' 618 ] 619 620 for (text of kTexts) { 621 const tm = ctx.measureText(text); 622 text_width = tm.width; 623 step = 30; 624 if ('0px' == '10px') { 625 step = 40; 626 } 627 628 offset = step; 629 adjusted_offset = alignOffset(offset, text_width); 630 tm_position = tm.getIndexFromOffset(adjusted_offset); 631 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 632 assert_equals(tm_position, 633 doc_position, 634 "for " + text + " offset " + offset); 635 636 offset = text_width / 2 - 10; 637 adjusted_offset = alignOffset(offset, text_width); 638 tm_position = tm.getIndexFromOffset(adjusted_offset); 639 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 640 assert_equals(tm_position, 641 doc_position, 642 "for " + text + " offset " + offset); 643 644 offset = text_width / 2 + 10; 645 adjusted_offset = alignOffset(offset, text_width); 646 tm_position = tm.getIndexFromOffset(adjusted_offset); 647 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 648 assert_equals(tm_position, 649 doc_position, 650 "for " + text + " offset " + offset); 651 652 offset = text_width - step; 653 adjusted_offset = alignOffset(offset, text_width); 654 tm_position = tm.getIndexFromOffset(adjusted_offset); 655 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 656 assert_equals(tm_position, 657 doc_position, 658 "for " + text + " offset " + offset); 659 } 660 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 0px letter spacing and no-directional-override."); 661 662 test(t => { 663 const canvas = document.createElement('canvas'); 664 canvas.width = 100; 665 canvas.height = 50; 666 const ctx = canvas.getContext('2d'); 667 668 function alignOffset(offset, width) { 669 if ('start' == 'center') { 670 offset -= width / 2; 671 } else if ('start' == 'right' || 672 ('ltr' == 'ltr' && 'start' == 'end') || 673 ('ltr' == 'rtl' && 'start' == 'start')) { 674 offset -= width; 675 } 676 return offset; 677 } 678 679 680 function placeAndMeasureTextInDOM(text, text_width, point) { 681 const el = document.createElement("p"); 682 el.innerHTML = text; 683 el.style.font = '50px sans-serif'; 684 el.style.direction = 'ltr'; 685 el.style.letterSpacing = '0px'; 686 // Put the text top left to make offsets simpler. 687 el.style.padding = '0px'; 688 el.style.margin = '0px'; 689 el.style.position = 'absolute'; 690 el.style.x = '0px'; 691 el.style.y = '0px'; 692 document.body.appendChild(el); 693 text_bound = el.getBoundingClientRect(); 694 text_x = text_bound.x; 695 text_y = text_bound.y + text_bound.height / 2; 696 697 // Offset to the requested point determined by textAlign and direction. 698 let text_align_dx = 0; 699 if ('start' == 'center') { 700 text_align_dx = text_width / 2; 701 } else if ('start' == 'right' || 702 ('ltr' == 'ltr' && 'start' == 'end') || 703 ('ltr' == 'rtl' && 'start' == 'start')) { 704 text_align_dx = text_width; 705 } 706 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 707 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 708 709 document.body.removeChild(el); 710 711 return position.offset; 712 } 713 714 ctx.font = '50px sans-serif'; 715 ctx.direction = 'ltr'; 716 ctx.textAlign = 'start'; 717 ctx.letterSpacing = '0px'; 718 719 const kTexts = [ 720 'UNAVAILABLE', 721 'ππΆπ', 722 'οΌοΌγοΌοΌ', 723 'οΌοΌγγοΌοΌ', 724 'γγοΌοΌγγ', 725 '--abcd__' 726 ] 727 728 for (text of kTexts) { 729 const tm = ctx.measureText(text); 730 text_width = tm.width; 731 step = 30; 732 if ('0px' == '10px') { 733 step = 40; 734 } 735 736 offset = step; 737 adjusted_offset = alignOffset(offset, text_width); 738 tm_position = tm.getIndexFromOffset(adjusted_offset); 739 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 740 assert_equals(tm_position, 741 doc_position, 742 "for " + text + " offset " + offset); 743 744 offset = text_width / 2 - 10; 745 adjusted_offset = alignOffset(offset, text_width); 746 tm_position = tm.getIndexFromOffset(adjusted_offset); 747 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 748 assert_equals(tm_position, 749 doc_position, 750 "for " + text + " offset " + offset); 751 752 offset = text_width / 2 + 10; 753 adjusted_offset = alignOffset(offset, text_width); 754 tm_position = tm.getIndexFromOffset(adjusted_offset); 755 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 756 assert_equals(tm_position, 757 doc_position, 758 "for " + text + " offset " + offset); 759 760 offset = text_width - step; 761 adjusted_offset = alignOffset(offset, text_width); 762 tm_position = tm.getIndexFromOffset(adjusted_offset); 763 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 764 assert_equals(tm_position, 765 doc_position, 766 "for " + text + " offset " + offset); 767 } 768 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 0px letter spacing and no-directional-override."); 769 770 test(t => { 771 const canvas = document.createElement('canvas'); 772 canvas.width = 100; 773 canvas.height = 50; 774 const ctx = canvas.getContext('2d'); 775 776 function alignOffset(offset, width) { 777 if ('start' == 'center') { 778 offset -= width / 2; 779 } else if ('start' == 'right' || 780 ('rtl' == 'ltr' && 'start' == 'end') || 781 ('rtl' == 'rtl' && 'start' == 'start')) { 782 offset -= width; 783 } 784 return offset; 785 } 786 787 788 function placeAndMeasureTextInDOM(text, text_width, point) { 789 const el = document.createElement("p"); 790 el.innerHTML = text; 791 el.style.font = '50px sans-serif'; 792 el.style.direction = 'rtl'; 793 el.style.letterSpacing = '0px'; 794 // Put the text top left to make offsets simpler. 795 el.style.padding = '0px'; 796 el.style.margin = '0px'; 797 el.style.position = 'absolute'; 798 el.style.x = '0px'; 799 el.style.y = '0px'; 800 document.body.appendChild(el); 801 text_bound = el.getBoundingClientRect(); 802 text_x = text_bound.x; 803 text_y = text_bound.y + text_bound.height / 2; 804 805 // Offset to the requested point determined by textAlign and direction. 806 let text_align_dx = 0; 807 if ('start' == 'center') { 808 text_align_dx = text_width / 2; 809 } else if ('start' == 'right' || 810 ('rtl' == 'ltr' && 'start' == 'end') || 811 ('rtl' == 'rtl' && 'start' == 'start')) { 812 text_align_dx = text_width; 813 } 814 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 815 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 816 817 document.body.removeChild(el); 818 819 return position.offset; 820 } 821 822 ctx.font = '50px sans-serif'; 823 ctx.direction = 'rtl'; 824 ctx.textAlign = 'start'; 825 ctx.letterSpacing = '0px'; 826 827 const kTexts = [ 828 'UNAVAILABLE', 829 'ππΆπ', 830 'οΌοΌγοΌοΌ', 831 'οΌοΌγγοΌοΌ', 832 'γγοΌοΌγγ', 833 '--abcd__' 834 ] 835 836 for (text of kTexts) { 837 const tm = ctx.measureText(text); 838 text_width = tm.width; 839 step = 30; 840 if ('0px' == '10px') { 841 step = 40; 842 } 843 844 offset = step; 845 adjusted_offset = alignOffset(offset, text_width); 846 tm_position = tm.getIndexFromOffset(adjusted_offset); 847 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 848 assert_equals(tm_position, 849 doc_position, 850 "for " + text + " offset " + offset); 851 852 offset = text_width / 2 - 10; 853 adjusted_offset = alignOffset(offset, text_width); 854 tm_position = tm.getIndexFromOffset(adjusted_offset); 855 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 856 assert_equals(tm_position, 857 doc_position, 858 "for " + text + " offset " + offset); 859 860 offset = text_width / 2 + 10; 861 adjusted_offset = alignOffset(offset, text_width); 862 tm_position = tm.getIndexFromOffset(adjusted_offset); 863 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 864 assert_equals(tm_position, 865 doc_position, 866 "for " + text + " offset " + offset); 867 868 offset = text_width - step; 869 adjusted_offset = alignOffset(offset, text_width); 870 tm_position = tm.getIndexFromOffset(adjusted_offset); 871 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 872 assert_equals(tm_position, 873 doc_position, 874 "for " + text + " offset " + offset); 875 } 876 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 0px letter spacing and no-directional-override."); 877 878 test(t => { 879 const canvas = document.createElement('canvas'); 880 canvas.width = 100; 881 canvas.height = 50; 882 const ctx = canvas.getContext('2d'); 883 884 function alignOffset(offset, width) { 885 if ('end' == 'center') { 886 offset -= width / 2; 887 } else if ('end' == 'right' || 888 ('ltr' == 'ltr' && 'end' == 'end') || 889 ('ltr' == 'rtl' && 'end' == 'start')) { 890 offset -= width; 891 } 892 return offset; 893 } 894 895 896 function placeAndMeasureTextInDOM(text, text_width, point) { 897 const el = document.createElement("p"); 898 el.innerHTML = text; 899 el.style.font = '50px sans-serif'; 900 el.style.direction = 'ltr'; 901 el.style.letterSpacing = '0px'; 902 // Put the text top left to make offsets simpler. 903 el.style.padding = '0px'; 904 el.style.margin = '0px'; 905 el.style.position = 'absolute'; 906 el.style.x = '0px'; 907 el.style.y = '0px'; 908 document.body.appendChild(el); 909 text_bound = el.getBoundingClientRect(); 910 text_x = text_bound.x; 911 text_y = text_bound.y + text_bound.height / 2; 912 913 // Offset to the requested point determined by textAlign and direction. 914 let text_align_dx = 0; 915 if ('end' == 'center') { 916 text_align_dx = text_width / 2; 917 } else if ('end' == 'right' || 918 ('ltr' == 'ltr' && 'end' == 'end') || 919 ('ltr' == 'rtl' && 'end' == 'start')) { 920 text_align_dx = text_width; 921 } 922 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 923 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 924 925 document.body.removeChild(el); 926 927 return position.offset; 928 } 929 930 ctx.font = '50px sans-serif'; 931 ctx.direction = 'ltr'; 932 ctx.textAlign = 'end'; 933 ctx.letterSpacing = '0px'; 934 935 const kTexts = [ 936 'UNAVAILABLE', 937 'ππΆπ', 938 'οΌοΌγοΌοΌ', 939 'οΌοΌγγοΌοΌ', 940 'γγοΌοΌγγ', 941 '--abcd__' 942 ] 943 944 for (text of kTexts) { 945 const tm = ctx.measureText(text); 946 text_width = tm.width; 947 step = 30; 948 if ('0px' == '10px') { 949 step = 40; 950 } 951 952 offset = step; 953 adjusted_offset = alignOffset(offset, text_width); 954 tm_position = tm.getIndexFromOffset(adjusted_offset); 955 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 956 assert_equals(tm_position, 957 doc_position, 958 "for " + text + " offset " + offset); 959 960 offset = text_width / 2 - 10; 961 adjusted_offset = alignOffset(offset, text_width); 962 tm_position = tm.getIndexFromOffset(adjusted_offset); 963 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 964 assert_equals(tm_position, 965 doc_position, 966 "for " + text + " offset " + offset); 967 968 offset = text_width / 2 + 10; 969 adjusted_offset = alignOffset(offset, text_width); 970 tm_position = tm.getIndexFromOffset(adjusted_offset); 971 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 972 assert_equals(tm_position, 973 doc_position, 974 "for " + text + " offset " + offset); 975 976 offset = text_width - step; 977 adjusted_offset = alignOffset(offset, text_width); 978 tm_position = tm.getIndexFromOffset(adjusted_offset); 979 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 980 assert_equals(tm_position, 981 doc_position, 982 "for " + text + " offset " + offset); 983 } 984 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 0px letter spacing and no-directional-override."); 985 986 test(t => { 987 const canvas = document.createElement('canvas'); 988 canvas.width = 100; 989 canvas.height = 50; 990 const ctx = canvas.getContext('2d'); 991 992 function alignOffset(offset, width) { 993 if ('end' == 'center') { 994 offset -= width / 2; 995 } else if ('end' == 'right' || 996 ('rtl' == 'ltr' && 'end' == 'end') || 997 ('rtl' == 'rtl' && 'end' == 'start')) { 998 offset -= width; 999 } 1000 return offset; 1001 } 1002 1003 1004 function placeAndMeasureTextInDOM(text, text_width, point) { 1005 const el = document.createElement("p"); 1006 el.innerHTML = text; 1007 el.style.font = '50px sans-serif'; 1008 el.style.direction = 'rtl'; 1009 el.style.letterSpacing = '0px'; 1010 // Put the text top left to make offsets simpler. 1011 el.style.padding = '0px'; 1012 el.style.margin = '0px'; 1013 el.style.position = 'absolute'; 1014 el.style.x = '0px'; 1015 el.style.y = '0px'; 1016 document.body.appendChild(el); 1017 text_bound = el.getBoundingClientRect(); 1018 text_x = text_bound.x; 1019 text_y = text_bound.y + text_bound.height / 2; 1020 1021 // Offset to the requested point determined by textAlign and direction. 1022 let text_align_dx = 0; 1023 if ('end' == 'center') { 1024 text_align_dx = text_width / 2; 1025 } else if ('end' == 'right' || 1026 ('rtl' == 'ltr' && 'end' == 'end') || 1027 ('rtl' == 'rtl' && 'end' == 'start')) { 1028 text_align_dx = text_width; 1029 } 1030 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1031 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1032 1033 document.body.removeChild(el); 1034 1035 return position.offset; 1036 } 1037 1038 ctx.font = '50px sans-serif'; 1039 ctx.direction = 'rtl'; 1040 ctx.textAlign = 'end'; 1041 ctx.letterSpacing = '0px'; 1042 1043 const kTexts = [ 1044 'UNAVAILABLE', 1045 'ππΆπ', 1046 'οΌοΌγοΌοΌ', 1047 'οΌοΌγγοΌοΌ', 1048 'γγοΌοΌγγ', 1049 '--abcd__' 1050 ] 1051 1052 for (text of kTexts) { 1053 const tm = ctx.measureText(text); 1054 text_width = tm.width; 1055 step = 30; 1056 if ('0px' == '10px') { 1057 step = 40; 1058 } 1059 1060 offset = step; 1061 adjusted_offset = alignOffset(offset, text_width); 1062 tm_position = tm.getIndexFromOffset(adjusted_offset); 1063 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1064 assert_equals(tm_position, 1065 doc_position, 1066 "for " + text + " offset " + offset); 1067 1068 offset = text_width / 2 - 10; 1069 adjusted_offset = alignOffset(offset, text_width); 1070 tm_position = tm.getIndexFromOffset(adjusted_offset); 1071 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1072 assert_equals(tm_position, 1073 doc_position, 1074 "for " + text + " offset " + offset); 1075 1076 offset = text_width / 2 + 10; 1077 adjusted_offset = alignOffset(offset, text_width); 1078 tm_position = tm.getIndexFromOffset(adjusted_offset); 1079 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1080 assert_equals(tm_position, 1081 doc_position, 1082 "for " + text + " offset " + offset); 1083 1084 offset = text_width - step; 1085 adjusted_offset = alignOffset(offset, text_width); 1086 tm_position = tm.getIndexFromOffset(adjusted_offset); 1087 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1088 assert_equals(tm_position, 1089 doc_position, 1090 "for " + text + " offset " + offset); 1091 } 1092 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 0px letter spacing and no-directional-override."); 1093 1094 test(t => { 1095 const canvas = document.createElement('canvas'); 1096 canvas.width = 100; 1097 canvas.height = 50; 1098 const ctx = canvas.getContext('2d'); 1099 1100 function alignOffset(offset, width) { 1101 if ('left' == 'center') { 1102 offset -= width / 2; 1103 } else if ('left' == 'right' || 1104 ('ltr' == 'ltr' && 'left' == 'end') || 1105 ('ltr' == 'rtl' && 'left' == 'start')) { 1106 offset -= width; 1107 } 1108 return offset; 1109 } 1110 1111 1112 function placeAndMeasureTextInDOM(text, text_width, point) { 1113 const el = document.createElement("p"); 1114 el.innerHTML = text; 1115 el.style.font = '50px sans-serif'; 1116 el.style.direction = 'ltr'; 1117 el.style.letterSpacing = '10px'; 1118 // Put the text top left to make offsets simpler. 1119 el.style.padding = '0px'; 1120 el.style.margin = '0px'; 1121 el.style.position = 'absolute'; 1122 el.style.x = '0px'; 1123 el.style.y = '0px'; 1124 document.body.appendChild(el); 1125 text_bound = el.getBoundingClientRect(); 1126 text_x = text_bound.x; 1127 text_y = text_bound.y + text_bound.height / 2; 1128 1129 // Offset to the requested point determined by textAlign and direction. 1130 let text_align_dx = 0; 1131 if ('left' == 'center') { 1132 text_align_dx = text_width / 2; 1133 } else if ('left' == 'right' || 1134 ('ltr' == 'ltr' && 'left' == 'end') || 1135 ('ltr' == 'rtl' && 'left' == 'start')) { 1136 text_align_dx = text_width; 1137 } 1138 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1139 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1140 1141 document.body.removeChild(el); 1142 1143 return position.offset; 1144 } 1145 1146 ctx.font = '50px sans-serif'; 1147 ctx.direction = 'ltr'; 1148 ctx.textAlign = 'left'; 1149 ctx.letterSpacing = '10px'; 1150 1151 const kTexts = [ 1152 'UNAVAILABLE', 1153 'ππΆπ', 1154 'οΌοΌγοΌοΌ', 1155 'οΌοΌγγοΌοΌ', 1156 'γγοΌοΌγγ', 1157 '--abcd__' 1158 ] 1159 1160 for (text of kTexts) { 1161 const tm = ctx.measureText(text); 1162 text_width = tm.width; 1163 step = 30; 1164 if ('10px' == '10px') { 1165 step = 40; 1166 } 1167 1168 offset = step; 1169 adjusted_offset = alignOffset(offset, text_width); 1170 tm_position = tm.getIndexFromOffset(adjusted_offset); 1171 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1172 assert_equals(tm_position, 1173 doc_position, 1174 "for " + text + " offset " + offset); 1175 1176 offset = text_width / 2 - 10; 1177 adjusted_offset = alignOffset(offset, text_width); 1178 tm_position = tm.getIndexFromOffset(adjusted_offset); 1179 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1180 assert_equals(tm_position, 1181 doc_position, 1182 "for " + text + " offset " + offset); 1183 1184 offset = text_width / 2 + 10; 1185 adjusted_offset = alignOffset(offset, text_width); 1186 tm_position = tm.getIndexFromOffset(adjusted_offset); 1187 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1188 assert_equals(tm_position, 1189 doc_position, 1190 "for " + text + " offset " + offset); 1191 1192 offset = text_width - step; 1193 adjusted_offset = alignOffset(offset, text_width); 1194 tm_position = tm.getIndexFromOffset(adjusted_offset); 1195 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1196 assert_equals(tm_position, 1197 doc_position, 1198 "for " + text + " offset " + offset); 1199 } 1200 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 10px letter spacing and no-directional-override."); 1201 1202 test(t => { 1203 const canvas = document.createElement('canvas'); 1204 canvas.width = 100; 1205 canvas.height = 50; 1206 const ctx = canvas.getContext('2d'); 1207 1208 function alignOffset(offset, width) { 1209 if ('left' == 'center') { 1210 offset -= width / 2; 1211 } else if ('left' == 'right' || 1212 ('rtl' == 'ltr' && 'left' == 'end') || 1213 ('rtl' == 'rtl' && 'left' == 'start')) { 1214 offset -= width; 1215 } 1216 return offset; 1217 } 1218 1219 1220 function placeAndMeasureTextInDOM(text, text_width, point) { 1221 const el = document.createElement("p"); 1222 el.innerHTML = text; 1223 el.style.font = '50px sans-serif'; 1224 el.style.direction = 'rtl'; 1225 el.style.letterSpacing = '10px'; 1226 // Put the text top left to make offsets simpler. 1227 el.style.padding = '0px'; 1228 el.style.margin = '0px'; 1229 el.style.position = 'absolute'; 1230 el.style.x = '0px'; 1231 el.style.y = '0px'; 1232 document.body.appendChild(el); 1233 text_bound = el.getBoundingClientRect(); 1234 text_x = text_bound.x; 1235 text_y = text_bound.y + text_bound.height / 2; 1236 1237 // Offset to the requested point determined by textAlign and direction. 1238 let text_align_dx = 0; 1239 if ('left' == 'center') { 1240 text_align_dx = text_width / 2; 1241 } else if ('left' == 'right' || 1242 ('rtl' == 'ltr' && 'left' == 'end') || 1243 ('rtl' == 'rtl' && 'left' == 'start')) { 1244 text_align_dx = text_width; 1245 } 1246 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1247 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1248 1249 document.body.removeChild(el); 1250 1251 return position.offset; 1252 } 1253 1254 ctx.font = '50px sans-serif'; 1255 ctx.direction = 'rtl'; 1256 ctx.textAlign = 'left'; 1257 ctx.letterSpacing = '10px'; 1258 1259 const kTexts = [ 1260 'UNAVAILABLE', 1261 'ππΆπ', 1262 'οΌοΌγοΌοΌ', 1263 'οΌοΌγγοΌοΌ', 1264 'γγοΌοΌγγ', 1265 '--abcd__' 1266 ] 1267 1268 for (text of kTexts) { 1269 const tm = ctx.measureText(text); 1270 text_width = tm.width; 1271 step = 30; 1272 if ('10px' == '10px') { 1273 step = 40; 1274 } 1275 1276 offset = step; 1277 adjusted_offset = alignOffset(offset, text_width); 1278 tm_position = tm.getIndexFromOffset(adjusted_offset); 1279 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1280 assert_equals(tm_position, 1281 doc_position, 1282 "for " + text + " offset " + offset); 1283 1284 offset = text_width / 2 - 10; 1285 adjusted_offset = alignOffset(offset, text_width); 1286 tm_position = tm.getIndexFromOffset(adjusted_offset); 1287 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1288 assert_equals(tm_position, 1289 doc_position, 1290 "for " + text + " offset " + offset); 1291 1292 offset = text_width / 2 + 10; 1293 adjusted_offset = alignOffset(offset, text_width); 1294 tm_position = tm.getIndexFromOffset(adjusted_offset); 1295 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1296 assert_equals(tm_position, 1297 doc_position, 1298 "for " + text + " offset " + offset); 1299 1300 offset = text_width - step; 1301 adjusted_offset = alignOffset(offset, text_width); 1302 tm_position = tm.getIndexFromOffset(adjusted_offset); 1303 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1304 assert_equals(tm_position, 1305 doc_position, 1306 "for " + text + " offset " + offset); 1307 } 1308 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 10px letter spacing and no-directional-override."); 1309 1310 test(t => { 1311 const canvas = document.createElement('canvas'); 1312 canvas.width = 100; 1313 canvas.height = 50; 1314 const ctx = canvas.getContext('2d'); 1315 1316 function alignOffset(offset, width) { 1317 if ('center' == 'center') { 1318 offset -= width / 2; 1319 } else if ('center' == 'right' || 1320 ('ltr' == 'ltr' && 'center' == 'end') || 1321 ('ltr' == 'rtl' && 'center' == 'start')) { 1322 offset -= width; 1323 } 1324 return offset; 1325 } 1326 1327 1328 function placeAndMeasureTextInDOM(text, text_width, point) { 1329 const el = document.createElement("p"); 1330 el.innerHTML = text; 1331 el.style.font = '50px sans-serif'; 1332 el.style.direction = 'ltr'; 1333 el.style.letterSpacing = '10px'; 1334 // Put the text top left to make offsets simpler. 1335 el.style.padding = '0px'; 1336 el.style.margin = '0px'; 1337 el.style.position = 'absolute'; 1338 el.style.x = '0px'; 1339 el.style.y = '0px'; 1340 document.body.appendChild(el); 1341 text_bound = el.getBoundingClientRect(); 1342 text_x = text_bound.x; 1343 text_y = text_bound.y + text_bound.height / 2; 1344 1345 // Offset to the requested point determined by textAlign and direction. 1346 let text_align_dx = 0; 1347 if ('center' == 'center') { 1348 text_align_dx = text_width / 2; 1349 } else if ('center' == 'right' || 1350 ('ltr' == 'ltr' && 'center' == 'end') || 1351 ('ltr' == 'rtl' && 'center' == 'start')) { 1352 text_align_dx = text_width; 1353 } 1354 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1355 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1356 1357 document.body.removeChild(el); 1358 1359 return position.offset; 1360 } 1361 1362 ctx.font = '50px sans-serif'; 1363 ctx.direction = 'ltr'; 1364 ctx.textAlign = 'center'; 1365 ctx.letterSpacing = '10px'; 1366 1367 const kTexts = [ 1368 'UNAVAILABLE', 1369 'ππΆπ', 1370 'οΌοΌγοΌοΌ', 1371 'οΌοΌγγοΌοΌ', 1372 'γγοΌοΌγγ', 1373 '--abcd__' 1374 ] 1375 1376 for (text of kTexts) { 1377 const tm = ctx.measureText(text); 1378 text_width = tm.width; 1379 step = 30; 1380 if ('10px' == '10px') { 1381 step = 40; 1382 } 1383 1384 offset = step; 1385 adjusted_offset = alignOffset(offset, text_width); 1386 tm_position = tm.getIndexFromOffset(adjusted_offset); 1387 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1388 assert_equals(tm_position, 1389 doc_position, 1390 "for " + text + " offset " + offset); 1391 1392 offset = text_width / 2 - 10; 1393 adjusted_offset = alignOffset(offset, text_width); 1394 tm_position = tm.getIndexFromOffset(adjusted_offset); 1395 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1396 assert_equals(tm_position, 1397 doc_position, 1398 "for " + text + " offset " + offset); 1399 1400 offset = text_width / 2 + 10; 1401 adjusted_offset = alignOffset(offset, text_width); 1402 tm_position = tm.getIndexFromOffset(adjusted_offset); 1403 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1404 assert_equals(tm_position, 1405 doc_position, 1406 "for " + text + " offset " + offset); 1407 1408 offset = text_width - step; 1409 adjusted_offset = alignOffset(offset, text_width); 1410 tm_position = tm.getIndexFromOffset(adjusted_offset); 1411 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1412 assert_equals(tm_position, 1413 doc_position, 1414 "for " + text + " offset " + offset); 1415 } 1416 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 10px letter spacing and no-directional-override."); 1417 1418 test(t => { 1419 const canvas = document.createElement('canvas'); 1420 canvas.width = 100; 1421 canvas.height = 50; 1422 const ctx = canvas.getContext('2d'); 1423 1424 function alignOffset(offset, width) { 1425 if ('center' == 'center') { 1426 offset -= width / 2; 1427 } else if ('center' == 'right' || 1428 ('rtl' == 'ltr' && 'center' == 'end') || 1429 ('rtl' == 'rtl' && 'center' == 'start')) { 1430 offset -= width; 1431 } 1432 return offset; 1433 } 1434 1435 1436 function placeAndMeasureTextInDOM(text, text_width, point) { 1437 const el = document.createElement("p"); 1438 el.innerHTML = text; 1439 el.style.font = '50px sans-serif'; 1440 el.style.direction = 'rtl'; 1441 el.style.letterSpacing = '10px'; 1442 // Put the text top left to make offsets simpler. 1443 el.style.padding = '0px'; 1444 el.style.margin = '0px'; 1445 el.style.position = 'absolute'; 1446 el.style.x = '0px'; 1447 el.style.y = '0px'; 1448 document.body.appendChild(el); 1449 text_bound = el.getBoundingClientRect(); 1450 text_x = text_bound.x; 1451 text_y = text_bound.y + text_bound.height / 2; 1452 1453 // Offset to the requested point determined by textAlign and direction. 1454 let text_align_dx = 0; 1455 if ('center' == 'center') { 1456 text_align_dx = text_width / 2; 1457 } else if ('center' == 'right' || 1458 ('rtl' == 'ltr' && 'center' == 'end') || 1459 ('rtl' == 'rtl' && 'center' == 'start')) { 1460 text_align_dx = text_width; 1461 } 1462 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1463 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1464 1465 document.body.removeChild(el); 1466 1467 return position.offset; 1468 } 1469 1470 ctx.font = '50px sans-serif'; 1471 ctx.direction = 'rtl'; 1472 ctx.textAlign = 'center'; 1473 ctx.letterSpacing = '10px'; 1474 1475 const kTexts = [ 1476 'UNAVAILABLE', 1477 'ππΆπ', 1478 'οΌοΌγοΌοΌ', 1479 'οΌοΌγγοΌοΌ', 1480 'γγοΌοΌγγ', 1481 '--abcd__' 1482 ] 1483 1484 for (text of kTexts) { 1485 const tm = ctx.measureText(text); 1486 text_width = tm.width; 1487 step = 30; 1488 if ('10px' == '10px') { 1489 step = 40; 1490 } 1491 1492 offset = step; 1493 adjusted_offset = alignOffset(offset, text_width); 1494 tm_position = tm.getIndexFromOffset(adjusted_offset); 1495 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1496 assert_equals(tm_position, 1497 doc_position, 1498 "for " + text + " offset " + offset); 1499 1500 offset = text_width / 2 - 10; 1501 adjusted_offset = alignOffset(offset, text_width); 1502 tm_position = tm.getIndexFromOffset(adjusted_offset); 1503 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1504 assert_equals(tm_position, 1505 doc_position, 1506 "for " + text + " offset " + offset); 1507 1508 offset = text_width / 2 + 10; 1509 adjusted_offset = alignOffset(offset, text_width); 1510 tm_position = tm.getIndexFromOffset(adjusted_offset); 1511 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1512 assert_equals(tm_position, 1513 doc_position, 1514 "for " + text + " offset " + offset); 1515 1516 offset = text_width - step; 1517 adjusted_offset = alignOffset(offset, text_width); 1518 tm_position = tm.getIndexFromOffset(adjusted_offset); 1519 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1520 assert_equals(tm_position, 1521 doc_position, 1522 "for " + text + " offset " + offset); 1523 } 1524 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 10px letter spacing and no-directional-override."); 1525 1526 test(t => { 1527 const canvas = document.createElement('canvas'); 1528 canvas.width = 100; 1529 canvas.height = 50; 1530 const ctx = canvas.getContext('2d'); 1531 1532 function alignOffset(offset, width) { 1533 if ('right' == 'center') { 1534 offset -= width / 2; 1535 } else if ('right' == 'right' || 1536 ('ltr' == 'ltr' && 'right' == 'end') || 1537 ('ltr' == 'rtl' && 'right' == 'start')) { 1538 offset -= width; 1539 } 1540 return offset; 1541 } 1542 1543 1544 function placeAndMeasureTextInDOM(text, text_width, point) { 1545 const el = document.createElement("p"); 1546 el.innerHTML = text; 1547 el.style.font = '50px sans-serif'; 1548 el.style.direction = 'ltr'; 1549 el.style.letterSpacing = '10px'; 1550 // Put the text top left to make offsets simpler. 1551 el.style.padding = '0px'; 1552 el.style.margin = '0px'; 1553 el.style.position = 'absolute'; 1554 el.style.x = '0px'; 1555 el.style.y = '0px'; 1556 document.body.appendChild(el); 1557 text_bound = el.getBoundingClientRect(); 1558 text_x = text_bound.x; 1559 text_y = text_bound.y + text_bound.height / 2; 1560 1561 // Offset to the requested point determined by textAlign and direction. 1562 let text_align_dx = 0; 1563 if ('right' == 'center') { 1564 text_align_dx = text_width / 2; 1565 } else if ('right' == 'right' || 1566 ('ltr' == 'ltr' && 'right' == 'end') || 1567 ('ltr' == 'rtl' && 'right' == 'start')) { 1568 text_align_dx = text_width; 1569 } 1570 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1571 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1572 1573 document.body.removeChild(el); 1574 1575 return position.offset; 1576 } 1577 1578 ctx.font = '50px sans-serif'; 1579 ctx.direction = 'ltr'; 1580 ctx.textAlign = 'right'; 1581 ctx.letterSpacing = '10px'; 1582 1583 const kTexts = [ 1584 'UNAVAILABLE', 1585 'ππΆπ', 1586 'οΌοΌγοΌοΌ', 1587 'οΌοΌγγοΌοΌ', 1588 'γγοΌοΌγγ', 1589 '--abcd__' 1590 ] 1591 1592 for (text of kTexts) { 1593 const tm = ctx.measureText(text); 1594 text_width = tm.width; 1595 step = 30; 1596 if ('10px' == '10px') { 1597 step = 40; 1598 } 1599 1600 offset = step; 1601 adjusted_offset = alignOffset(offset, text_width); 1602 tm_position = tm.getIndexFromOffset(adjusted_offset); 1603 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1604 assert_equals(tm_position, 1605 doc_position, 1606 "for " + text + " offset " + offset); 1607 1608 offset = text_width / 2 - 10; 1609 adjusted_offset = alignOffset(offset, text_width); 1610 tm_position = tm.getIndexFromOffset(adjusted_offset); 1611 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1612 assert_equals(tm_position, 1613 doc_position, 1614 "for " + text + " offset " + offset); 1615 1616 offset = text_width / 2 + 10; 1617 adjusted_offset = alignOffset(offset, text_width); 1618 tm_position = tm.getIndexFromOffset(adjusted_offset); 1619 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1620 assert_equals(tm_position, 1621 doc_position, 1622 "for " + text + " offset " + offset); 1623 1624 offset = text_width - step; 1625 adjusted_offset = alignOffset(offset, text_width); 1626 tm_position = tm.getIndexFromOffset(adjusted_offset); 1627 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1628 assert_equals(tm_position, 1629 doc_position, 1630 "for " + text + " offset " + offset); 1631 } 1632 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 10px letter spacing and no-directional-override."); 1633 1634 test(t => { 1635 const canvas = document.createElement('canvas'); 1636 canvas.width = 100; 1637 canvas.height = 50; 1638 const ctx = canvas.getContext('2d'); 1639 1640 function alignOffset(offset, width) { 1641 if ('right' == 'center') { 1642 offset -= width / 2; 1643 } else if ('right' == 'right' || 1644 ('rtl' == 'ltr' && 'right' == 'end') || 1645 ('rtl' == 'rtl' && 'right' == 'start')) { 1646 offset -= width; 1647 } 1648 return offset; 1649 } 1650 1651 1652 function placeAndMeasureTextInDOM(text, text_width, point) { 1653 const el = document.createElement("p"); 1654 el.innerHTML = text; 1655 el.style.font = '50px sans-serif'; 1656 el.style.direction = 'rtl'; 1657 el.style.letterSpacing = '10px'; 1658 // Put the text top left to make offsets simpler. 1659 el.style.padding = '0px'; 1660 el.style.margin = '0px'; 1661 el.style.position = 'absolute'; 1662 el.style.x = '0px'; 1663 el.style.y = '0px'; 1664 document.body.appendChild(el); 1665 text_bound = el.getBoundingClientRect(); 1666 text_x = text_bound.x; 1667 text_y = text_bound.y + text_bound.height / 2; 1668 1669 // Offset to the requested point determined by textAlign and direction. 1670 let text_align_dx = 0; 1671 if ('right' == 'center') { 1672 text_align_dx = text_width / 2; 1673 } else if ('right' == 'right' || 1674 ('rtl' == 'ltr' && 'right' == 'end') || 1675 ('rtl' == 'rtl' && 'right' == 'start')) { 1676 text_align_dx = text_width; 1677 } 1678 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1679 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1680 1681 document.body.removeChild(el); 1682 1683 return position.offset; 1684 } 1685 1686 ctx.font = '50px sans-serif'; 1687 ctx.direction = 'rtl'; 1688 ctx.textAlign = 'right'; 1689 ctx.letterSpacing = '10px'; 1690 1691 const kTexts = [ 1692 'UNAVAILABLE', 1693 'ππΆπ', 1694 'οΌοΌγοΌοΌ', 1695 'οΌοΌγγοΌοΌ', 1696 'γγοΌοΌγγ', 1697 '--abcd__' 1698 ] 1699 1700 for (text of kTexts) { 1701 const tm = ctx.measureText(text); 1702 text_width = tm.width; 1703 step = 30; 1704 if ('10px' == '10px') { 1705 step = 40; 1706 } 1707 1708 offset = step; 1709 adjusted_offset = alignOffset(offset, text_width); 1710 tm_position = tm.getIndexFromOffset(adjusted_offset); 1711 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1712 assert_equals(tm_position, 1713 doc_position, 1714 "for " + text + " offset " + offset); 1715 1716 offset = text_width / 2 - 10; 1717 adjusted_offset = alignOffset(offset, text_width); 1718 tm_position = tm.getIndexFromOffset(adjusted_offset); 1719 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1720 assert_equals(tm_position, 1721 doc_position, 1722 "for " + text + " offset " + offset); 1723 1724 offset = text_width / 2 + 10; 1725 adjusted_offset = alignOffset(offset, text_width); 1726 tm_position = tm.getIndexFromOffset(adjusted_offset); 1727 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1728 assert_equals(tm_position, 1729 doc_position, 1730 "for " + text + " offset " + offset); 1731 1732 offset = text_width - step; 1733 adjusted_offset = alignOffset(offset, text_width); 1734 tm_position = tm.getIndexFromOffset(adjusted_offset); 1735 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1736 assert_equals(tm_position, 1737 doc_position, 1738 "for " + text + " offset " + offset); 1739 } 1740 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 10px letter spacing and no-directional-override."); 1741 1742 test(t => { 1743 const canvas = document.createElement('canvas'); 1744 canvas.width = 100; 1745 canvas.height = 50; 1746 const ctx = canvas.getContext('2d'); 1747 1748 function alignOffset(offset, width) { 1749 if ('start' == 'center') { 1750 offset -= width / 2; 1751 } else if ('start' == 'right' || 1752 ('ltr' == 'ltr' && 'start' == 'end') || 1753 ('ltr' == 'rtl' && 'start' == 'start')) { 1754 offset -= width; 1755 } 1756 return offset; 1757 } 1758 1759 1760 function placeAndMeasureTextInDOM(text, text_width, point) { 1761 const el = document.createElement("p"); 1762 el.innerHTML = text; 1763 el.style.font = '50px sans-serif'; 1764 el.style.direction = 'ltr'; 1765 el.style.letterSpacing = '10px'; 1766 // Put the text top left to make offsets simpler. 1767 el.style.padding = '0px'; 1768 el.style.margin = '0px'; 1769 el.style.position = 'absolute'; 1770 el.style.x = '0px'; 1771 el.style.y = '0px'; 1772 document.body.appendChild(el); 1773 text_bound = el.getBoundingClientRect(); 1774 text_x = text_bound.x; 1775 text_y = text_bound.y + text_bound.height / 2; 1776 1777 // Offset to the requested point determined by textAlign and direction. 1778 let text_align_dx = 0; 1779 if ('start' == 'center') { 1780 text_align_dx = text_width / 2; 1781 } else if ('start' == 'right' || 1782 ('ltr' == 'ltr' && 'start' == 'end') || 1783 ('ltr' == 'rtl' && 'start' == 'start')) { 1784 text_align_dx = text_width; 1785 } 1786 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1787 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1788 1789 document.body.removeChild(el); 1790 1791 return position.offset; 1792 } 1793 1794 ctx.font = '50px sans-serif'; 1795 ctx.direction = 'ltr'; 1796 ctx.textAlign = 'start'; 1797 ctx.letterSpacing = '10px'; 1798 1799 const kTexts = [ 1800 'UNAVAILABLE', 1801 'ππΆπ', 1802 'οΌοΌγοΌοΌ', 1803 'οΌοΌγγοΌοΌ', 1804 'γγοΌοΌγγ', 1805 '--abcd__' 1806 ] 1807 1808 for (text of kTexts) { 1809 const tm = ctx.measureText(text); 1810 text_width = tm.width; 1811 step = 30; 1812 if ('10px' == '10px') { 1813 step = 40; 1814 } 1815 1816 offset = step; 1817 adjusted_offset = alignOffset(offset, text_width); 1818 tm_position = tm.getIndexFromOffset(adjusted_offset); 1819 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1820 assert_equals(tm_position, 1821 doc_position, 1822 "for " + text + " offset " + offset); 1823 1824 offset = text_width / 2 - 10; 1825 adjusted_offset = alignOffset(offset, text_width); 1826 tm_position = tm.getIndexFromOffset(adjusted_offset); 1827 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1828 assert_equals(tm_position, 1829 doc_position, 1830 "for " + text + " offset " + offset); 1831 1832 offset = text_width / 2 + 10; 1833 adjusted_offset = alignOffset(offset, text_width); 1834 tm_position = tm.getIndexFromOffset(adjusted_offset); 1835 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1836 assert_equals(tm_position, 1837 doc_position, 1838 "for " + text + " offset " + offset); 1839 1840 offset = text_width - step; 1841 adjusted_offset = alignOffset(offset, text_width); 1842 tm_position = tm.getIndexFromOffset(adjusted_offset); 1843 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1844 assert_equals(tm_position, 1845 doc_position, 1846 "for " + text + " offset " + offset); 1847 } 1848 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 10px letter spacing and no-directional-override."); 1849 1850 test(t => { 1851 const canvas = document.createElement('canvas'); 1852 canvas.width = 100; 1853 canvas.height = 50; 1854 const ctx = canvas.getContext('2d'); 1855 1856 function alignOffset(offset, width) { 1857 if ('start' == 'center') { 1858 offset -= width / 2; 1859 } else if ('start' == 'right' || 1860 ('rtl' == 'ltr' && 'start' == 'end') || 1861 ('rtl' == 'rtl' && 'start' == 'start')) { 1862 offset -= width; 1863 } 1864 return offset; 1865 } 1866 1867 1868 function placeAndMeasureTextInDOM(text, text_width, point) { 1869 const el = document.createElement("p"); 1870 el.innerHTML = text; 1871 el.style.font = '50px sans-serif'; 1872 el.style.direction = 'rtl'; 1873 el.style.letterSpacing = '10px'; 1874 // Put the text top left to make offsets simpler. 1875 el.style.padding = '0px'; 1876 el.style.margin = '0px'; 1877 el.style.position = 'absolute'; 1878 el.style.x = '0px'; 1879 el.style.y = '0px'; 1880 document.body.appendChild(el); 1881 text_bound = el.getBoundingClientRect(); 1882 text_x = text_bound.x; 1883 text_y = text_bound.y + text_bound.height / 2; 1884 1885 // Offset to the requested point determined by textAlign and direction. 1886 let text_align_dx = 0; 1887 if ('start' == 'center') { 1888 text_align_dx = text_width / 2; 1889 } else if ('start' == 'right' || 1890 ('rtl' == 'ltr' && 'start' == 'end') || 1891 ('rtl' == 'rtl' && 'start' == 'start')) { 1892 text_align_dx = text_width; 1893 } 1894 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 1895 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 1896 1897 document.body.removeChild(el); 1898 1899 return position.offset; 1900 } 1901 1902 ctx.font = '50px sans-serif'; 1903 ctx.direction = 'rtl'; 1904 ctx.textAlign = 'start'; 1905 ctx.letterSpacing = '10px'; 1906 1907 const kTexts = [ 1908 'UNAVAILABLE', 1909 'ππΆπ', 1910 'οΌοΌγοΌοΌ', 1911 'οΌοΌγγοΌοΌ', 1912 'γγοΌοΌγγ', 1913 '--abcd__' 1914 ] 1915 1916 for (text of kTexts) { 1917 const tm = ctx.measureText(text); 1918 text_width = tm.width; 1919 step = 30; 1920 if ('10px' == '10px') { 1921 step = 40; 1922 } 1923 1924 offset = step; 1925 adjusted_offset = alignOffset(offset, text_width); 1926 tm_position = tm.getIndexFromOffset(adjusted_offset); 1927 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1928 assert_equals(tm_position, 1929 doc_position, 1930 "for " + text + " offset " + offset); 1931 1932 offset = text_width / 2 - 10; 1933 adjusted_offset = alignOffset(offset, text_width); 1934 tm_position = tm.getIndexFromOffset(adjusted_offset); 1935 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1936 assert_equals(tm_position, 1937 doc_position, 1938 "for " + text + " offset " + offset); 1939 1940 offset = text_width / 2 + 10; 1941 adjusted_offset = alignOffset(offset, text_width); 1942 tm_position = tm.getIndexFromOffset(adjusted_offset); 1943 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1944 assert_equals(tm_position, 1945 doc_position, 1946 "for " + text + " offset " + offset); 1947 1948 offset = text_width - step; 1949 adjusted_offset = alignOffset(offset, text_width); 1950 tm_position = tm.getIndexFromOffset(adjusted_offset); 1951 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 1952 assert_equals(tm_position, 1953 doc_position, 1954 "for " + text + " offset " + offset); 1955 } 1956 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 10px letter spacing and no-directional-override."); 1957 1958 test(t => { 1959 const canvas = document.createElement('canvas'); 1960 canvas.width = 100; 1961 canvas.height = 50; 1962 const ctx = canvas.getContext('2d'); 1963 1964 function alignOffset(offset, width) { 1965 if ('end' == 'center') { 1966 offset -= width / 2; 1967 } else if ('end' == 'right' || 1968 ('ltr' == 'ltr' && 'end' == 'end') || 1969 ('ltr' == 'rtl' && 'end' == 'start')) { 1970 offset -= width; 1971 } 1972 return offset; 1973 } 1974 1975 1976 function placeAndMeasureTextInDOM(text, text_width, point) { 1977 const el = document.createElement("p"); 1978 el.innerHTML = text; 1979 el.style.font = '50px sans-serif'; 1980 el.style.direction = 'ltr'; 1981 el.style.letterSpacing = '10px'; 1982 // Put the text top left to make offsets simpler. 1983 el.style.padding = '0px'; 1984 el.style.margin = '0px'; 1985 el.style.position = 'absolute'; 1986 el.style.x = '0px'; 1987 el.style.y = '0px'; 1988 document.body.appendChild(el); 1989 text_bound = el.getBoundingClientRect(); 1990 text_x = text_bound.x; 1991 text_y = text_bound.y + text_bound.height / 2; 1992 1993 // Offset to the requested point determined by textAlign and direction. 1994 let text_align_dx = 0; 1995 if ('end' == 'center') { 1996 text_align_dx = text_width / 2; 1997 } else if ('end' == 'right' || 1998 ('ltr' == 'ltr' && 'end' == 'end') || 1999 ('ltr' == 'rtl' && 'end' == 'start')) { 2000 text_align_dx = text_width; 2001 } 2002 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2003 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2004 2005 document.body.removeChild(el); 2006 2007 return position.offset; 2008 } 2009 2010 ctx.font = '50px sans-serif'; 2011 ctx.direction = 'ltr'; 2012 ctx.textAlign = 'end'; 2013 ctx.letterSpacing = '10px'; 2014 2015 const kTexts = [ 2016 'UNAVAILABLE', 2017 'ππΆπ', 2018 'οΌοΌγοΌοΌ', 2019 'οΌοΌγγοΌοΌ', 2020 'γγοΌοΌγγ', 2021 '--abcd__' 2022 ] 2023 2024 for (text of kTexts) { 2025 const tm = ctx.measureText(text); 2026 text_width = tm.width; 2027 step = 30; 2028 if ('10px' == '10px') { 2029 step = 40; 2030 } 2031 2032 offset = step; 2033 adjusted_offset = alignOffset(offset, text_width); 2034 tm_position = tm.getIndexFromOffset(adjusted_offset); 2035 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2036 assert_equals(tm_position, 2037 doc_position, 2038 "for " + text + " offset " + offset); 2039 2040 offset = text_width / 2 - 10; 2041 adjusted_offset = alignOffset(offset, text_width); 2042 tm_position = tm.getIndexFromOffset(adjusted_offset); 2043 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2044 assert_equals(tm_position, 2045 doc_position, 2046 "for " + text + " offset " + offset); 2047 2048 offset = text_width / 2 + 10; 2049 adjusted_offset = alignOffset(offset, text_width); 2050 tm_position = tm.getIndexFromOffset(adjusted_offset); 2051 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2052 assert_equals(tm_position, 2053 doc_position, 2054 "for " + text + " offset " + offset); 2055 2056 offset = text_width - step; 2057 adjusted_offset = alignOffset(offset, text_width); 2058 tm_position = tm.getIndexFromOffset(adjusted_offset); 2059 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2060 assert_equals(tm_position, 2061 doc_position, 2062 "for " + text + " offset " + offset); 2063 } 2064 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 10px letter spacing and no-directional-override."); 2065 2066 test(t => { 2067 const canvas = document.createElement('canvas'); 2068 canvas.width = 100; 2069 canvas.height = 50; 2070 const ctx = canvas.getContext('2d'); 2071 2072 function alignOffset(offset, width) { 2073 if ('end' == 'center') { 2074 offset -= width / 2; 2075 } else if ('end' == 'right' || 2076 ('rtl' == 'ltr' && 'end' == 'end') || 2077 ('rtl' == 'rtl' && 'end' == 'start')) { 2078 offset -= width; 2079 } 2080 return offset; 2081 } 2082 2083 2084 function placeAndMeasureTextInDOM(text, text_width, point) { 2085 const el = document.createElement("p"); 2086 el.innerHTML = text; 2087 el.style.font = '50px sans-serif'; 2088 el.style.direction = 'rtl'; 2089 el.style.letterSpacing = '10px'; 2090 // Put the text top left to make offsets simpler. 2091 el.style.padding = '0px'; 2092 el.style.margin = '0px'; 2093 el.style.position = 'absolute'; 2094 el.style.x = '0px'; 2095 el.style.y = '0px'; 2096 document.body.appendChild(el); 2097 text_bound = el.getBoundingClientRect(); 2098 text_x = text_bound.x; 2099 text_y = text_bound.y + text_bound.height / 2; 2100 2101 // Offset to the requested point determined by textAlign and direction. 2102 let text_align_dx = 0; 2103 if ('end' == 'center') { 2104 text_align_dx = text_width / 2; 2105 } else if ('end' == 'right' || 2106 ('rtl' == 'ltr' && 'end' == 'end') || 2107 ('rtl' == 'rtl' && 'end' == 'start')) { 2108 text_align_dx = text_width; 2109 } 2110 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2111 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2112 2113 document.body.removeChild(el); 2114 2115 return position.offset; 2116 } 2117 2118 ctx.font = '50px sans-serif'; 2119 ctx.direction = 'rtl'; 2120 ctx.textAlign = 'end'; 2121 ctx.letterSpacing = '10px'; 2122 2123 const kTexts = [ 2124 'UNAVAILABLE', 2125 'ππΆπ', 2126 'οΌοΌγοΌοΌ', 2127 'οΌοΌγγοΌοΌ', 2128 'γγοΌοΌγγ', 2129 '--abcd__' 2130 ] 2131 2132 for (text of kTexts) { 2133 const tm = ctx.measureText(text); 2134 text_width = tm.width; 2135 step = 30; 2136 if ('10px' == '10px') { 2137 step = 40; 2138 } 2139 2140 offset = step; 2141 adjusted_offset = alignOffset(offset, text_width); 2142 tm_position = tm.getIndexFromOffset(adjusted_offset); 2143 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2144 assert_equals(tm_position, 2145 doc_position, 2146 "for " + text + " offset " + offset); 2147 2148 offset = text_width / 2 - 10; 2149 adjusted_offset = alignOffset(offset, text_width); 2150 tm_position = tm.getIndexFromOffset(adjusted_offset); 2151 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2152 assert_equals(tm_position, 2153 doc_position, 2154 "for " + text + " offset " + offset); 2155 2156 offset = text_width / 2 + 10; 2157 adjusted_offset = alignOffset(offset, text_width); 2158 tm_position = tm.getIndexFromOffset(adjusted_offset); 2159 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2160 assert_equals(tm_position, 2161 doc_position, 2162 "for " + text + " offset " + offset); 2163 2164 offset = text_width - step; 2165 adjusted_offset = alignOffset(offset, text_width); 2166 tm_position = tm.getIndexFromOffset(adjusted_offset); 2167 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2168 assert_equals(tm_position, 2169 doc_position, 2170 "for " + text + " offset " + offset); 2171 } 2172 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 10px letter spacing and no-directional-override."); 2173 2174 test(t => { 2175 const canvas = document.createElement('canvas'); 2176 canvas.width = 100; 2177 canvas.height = 50; 2178 const ctx = canvas.getContext('2d'); 2179 2180 function alignOffset(offset, width) { 2181 if ('left' == 'center') { 2182 offset -= width / 2; 2183 } else if ('left' == 'right' || 2184 ('ltr' == 'ltr' && 'left' == 'end') || 2185 ('ltr' == 'rtl' && 'left' == 'start')) { 2186 offset -= width; 2187 } 2188 return offset; 2189 } 2190 2191 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 2192 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 2193 const LTR_OVERRIDE = '\u202D'; 2194 const RTL_OVERRIDE = '\u202E'; 2195 const OVERRIDE_END = '\u202C'; 2196 if (direction_is_ltr) { 2197 return LTR_OVERRIDE + text + OVERRIDE_END; 2198 } 2199 return RTL_OVERRIDE + text + OVERRIDE_END; 2200 } 2201 2202 function placeAndMeasureTextInDOM(text, text_width, point) { 2203 const el = document.createElement("p"); 2204 el.innerHTML = text; 2205 el.style.font = '50px sans-serif'; 2206 el.style.direction = 'ltr'; 2207 el.style.letterSpacing = '0px'; 2208 // Put the text top left to make offsets simpler. 2209 el.style.padding = '0px'; 2210 el.style.margin = '0px'; 2211 el.style.position = 'absolute'; 2212 el.style.x = '0px'; 2213 el.style.y = '0px'; 2214 document.body.appendChild(el); 2215 text_bound = el.getBoundingClientRect(); 2216 text_x = text_bound.x; 2217 text_y = text_bound.y + text_bound.height / 2; 2218 2219 // Offset to the requested point determined by textAlign and direction. 2220 let text_align_dx = 0; 2221 if ('left' == 'center') { 2222 text_align_dx = text_width / 2; 2223 } else if ('left' == 'right' || 2224 ('ltr' == 'ltr' && 'left' == 'end') || 2225 ('ltr' == 'rtl' && 'left' == 'start')) { 2226 text_align_dx = text_width; 2227 } 2228 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2229 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2230 2231 document.body.removeChild(el); 2232 2233 return position.offset; 2234 } 2235 2236 ctx.font = '50px sans-serif'; 2237 ctx.direction = 'ltr'; 2238 ctx.textAlign = 'left'; 2239 ctx.letterSpacing = '0px'; 2240 2241 const kTexts = [ 2242 'UNAVAILABLE', 2243 'ππΆπ', 2244 'οΌοΌγοΌοΌ', 2245 'οΌοΌγγοΌοΌ', 2246 'γγοΌοΌγγ', 2247 '--abcd__' 2248 ] 2249 2250 for (text of kTexts) { 2251 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 2252 const tm = ctx.measureText(text); 2253 text_width = tm.width; 2254 step = 30; 2255 if ('0px' == '10px') { 2256 step = 40; 2257 } 2258 2259 offset = step; 2260 adjusted_offset = alignOffset(offset, text_width); 2261 tm_position = tm.getIndexFromOffset(adjusted_offset); 2262 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2263 assert_equals(tm_position, 2264 doc_position, 2265 "for " + text + " offset " + offset); 2266 2267 offset = text_width / 2 - 10; 2268 adjusted_offset = alignOffset(offset, text_width); 2269 tm_position = tm.getIndexFromOffset(adjusted_offset); 2270 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2271 assert_equals(tm_position, 2272 doc_position, 2273 "for " + text + " offset " + offset); 2274 2275 offset = text_width / 2 + 10; 2276 adjusted_offset = alignOffset(offset, text_width); 2277 tm_position = tm.getIndexFromOffset(adjusted_offset); 2278 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2279 assert_equals(tm_position, 2280 doc_position, 2281 "for " + text + " offset " + offset); 2282 2283 offset = text_width - step; 2284 adjusted_offset = alignOffset(offset, text_width); 2285 tm_position = tm.getIndexFromOffset(adjusted_offset); 2286 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2287 assert_equals(tm_position, 2288 doc_position, 2289 "for " + text + " offset " + offset); 2290 } 2291 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 0px letter spacing and directional-override."); 2292 2293 test(t => { 2294 const canvas = document.createElement('canvas'); 2295 canvas.width = 100; 2296 canvas.height = 50; 2297 const ctx = canvas.getContext('2d'); 2298 2299 function alignOffset(offset, width) { 2300 if ('left' == 'center') { 2301 offset -= width / 2; 2302 } else if ('left' == 'right' || 2303 ('rtl' == 'ltr' && 'left' == 'end') || 2304 ('rtl' == 'rtl' && 'left' == 'start')) { 2305 offset -= width; 2306 } 2307 return offset; 2308 } 2309 2310 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 2311 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 2312 const LTR_OVERRIDE = '\u202D'; 2313 const RTL_OVERRIDE = '\u202E'; 2314 const OVERRIDE_END = '\u202C'; 2315 if (direction_is_ltr) { 2316 return LTR_OVERRIDE + text + OVERRIDE_END; 2317 } 2318 return RTL_OVERRIDE + text + OVERRIDE_END; 2319 } 2320 2321 function placeAndMeasureTextInDOM(text, text_width, point) { 2322 const el = document.createElement("p"); 2323 el.innerHTML = text; 2324 el.style.font = '50px sans-serif'; 2325 el.style.direction = 'rtl'; 2326 el.style.letterSpacing = '0px'; 2327 // Put the text top left to make offsets simpler. 2328 el.style.padding = '0px'; 2329 el.style.margin = '0px'; 2330 el.style.position = 'absolute'; 2331 el.style.x = '0px'; 2332 el.style.y = '0px'; 2333 document.body.appendChild(el); 2334 text_bound = el.getBoundingClientRect(); 2335 text_x = text_bound.x; 2336 text_y = text_bound.y + text_bound.height / 2; 2337 2338 // Offset to the requested point determined by textAlign and direction. 2339 let text_align_dx = 0; 2340 if ('left' == 'center') { 2341 text_align_dx = text_width / 2; 2342 } else if ('left' == 'right' || 2343 ('rtl' == 'ltr' && 'left' == 'end') || 2344 ('rtl' == 'rtl' && 'left' == 'start')) { 2345 text_align_dx = text_width; 2346 } 2347 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2348 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2349 2350 document.body.removeChild(el); 2351 2352 return position.offset; 2353 } 2354 2355 ctx.font = '50px sans-serif'; 2356 ctx.direction = 'rtl'; 2357 ctx.textAlign = 'left'; 2358 ctx.letterSpacing = '0px'; 2359 2360 const kTexts = [ 2361 'UNAVAILABLE', 2362 'ππΆπ', 2363 'οΌοΌγοΌοΌ', 2364 'οΌοΌγγοΌοΌ', 2365 'γγοΌοΌγγ', 2366 '--abcd__' 2367 ] 2368 2369 for (text of kTexts) { 2370 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 2371 const tm = ctx.measureText(text); 2372 text_width = tm.width; 2373 step = 30; 2374 if ('0px' == '10px') { 2375 step = 40; 2376 } 2377 2378 offset = step; 2379 adjusted_offset = alignOffset(offset, text_width); 2380 tm_position = tm.getIndexFromOffset(adjusted_offset); 2381 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2382 assert_equals(tm_position, 2383 doc_position, 2384 "for " + text + " offset " + offset); 2385 2386 offset = text_width / 2 - 10; 2387 adjusted_offset = alignOffset(offset, text_width); 2388 tm_position = tm.getIndexFromOffset(adjusted_offset); 2389 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2390 assert_equals(tm_position, 2391 doc_position, 2392 "for " + text + " offset " + offset); 2393 2394 offset = text_width / 2 + 10; 2395 adjusted_offset = alignOffset(offset, text_width); 2396 tm_position = tm.getIndexFromOffset(adjusted_offset); 2397 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2398 assert_equals(tm_position, 2399 doc_position, 2400 "for " + text + " offset " + offset); 2401 2402 offset = text_width - step; 2403 adjusted_offset = alignOffset(offset, text_width); 2404 tm_position = tm.getIndexFromOffset(adjusted_offset); 2405 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2406 assert_equals(tm_position, 2407 doc_position, 2408 "for " + text + " offset " + offset); 2409 } 2410 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 0px letter spacing and directional-override."); 2411 2412 test(t => { 2413 const canvas = document.createElement('canvas'); 2414 canvas.width = 100; 2415 canvas.height = 50; 2416 const ctx = canvas.getContext('2d'); 2417 2418 function alignOffset(offset, width) { 2419 if ('center' == 'center') { 2420 offset -= width / 2; 2421 } else if ('center' == 'right' || 2422 ('ltr' == 'ltr' && 'center' == 'end') || 2423 ('ltr' == 'rtl' && 'center' == 'start')) { 2424 offset -= width; 2425 } 2426 return offset; 2427 } 2428 2429 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 2430 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 2431 const LTR_OVERRIDE = '\u202D'; 2432 const RTL_OVERRIDE = '\u202E'; 2433 const OVERRIDE_END = '\u202C'; 2434 if (direction_is_ltr) { 2435 return LTR_OVERRIDE + text + OVERRIDE_END; 2436 } 2437 return RTL_OVERRIDE + text + OVERRIDE_END; 2438 } 2439 2440 function placeAndMeasureTextInDOM(text, text_width, point) { 2441 const el = document.createElement("p"); 2442 el.innerHTML = text; 2443 el.style.font = '50px sans-serif'; 2444 el.style.direction = 'ltr'; 2445 el.style.letterSpacing = '0px'; 2446 // Put the text top left to make offsets simpler. 2447 el.style.padding = '0px'; 2448 el.style.margin = '0px'; 2449 el.style.position = 'absolute'; 2450 el.style.x = '0px'; 2451 el.style.y = '0px'; 2452 document.body.appendChild(el); 2453 text_bound = el.getBoundingClientRect(); 2454 text_x = text_bound.x; 2455 text_y = text_bound.y + text_bound.height / 2; 2456 2457 // Offset to the requested point determined by textAlign and direction. 2458 let text_align_dx = 0; 2459 if ('center' == 'center') { 2460 text_align_dx = text_width / 2; 2461 } else if ('center' == 'right' || 2462 ('ltr' == 'ltr' && 'center' == 'end') || 2463 ('ltr' == 'rtl' && 'center' == 'start')) { 2464 text_align_dx = text_width; 2465 } 2466 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2467 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2468 2469 document.body.removeChild(el); 2470 2471 return position.offset; 2472 } 2473 2474 ctx.font = '50px sans-serif'; 2475 ctx.direction = 'ltr'; 2476 ctx.textAlign = 'center'; 2477 ctx.letterSpacing = '0px'; 2478 2479 const kTexts = [ 2480 'UNAVAILABLE', 2481 'ππΆπ', 2482 'οΌοΌγοΌοΌ', 2483 'οΌοΌγγοΌοΌ', 2484 'γγοΌοΌγγ', 2485 '--abcd__' 2486 ] 2487 2488 for (text of kTexts) { 2489 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 2490 const tm = ctx.measureText(text); 2491 text_width = tm.width; 2492 step = 30; 2493 if ('0px' == '10px') { 2494 step = 40; 2495 } 2496 2497 offset = step; 2498 adjusted_offset = alignOffset(offset, text_width); 2499 tm_position = tm.getIndexFromOffset(adjusted_offset); 2500 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2501 assert_equals(tm_position, 2502 doc_position, 2503 "for " + text + " offset " + offset); 2504 2505 offset = text_width / 2 - 10; 2506 adjusted_offset = alignOffset(offset, text_width); 2507 tm_position = tm.getIndexFromOffset(adjusted_offset); 2508 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2509 assert_equals(tm_position, 2510 doc_position, 2511 "for " + text + " offset " + offset); 2512 2513 offset = text_width / 2 + 10; 2514 adjusted_offset = alignOffset(offset, text_width); 2515 tm_position = tm.getIndexFromOffset(adjusted_offset); 2516 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2517 assert_equals(tm_position, 2518 doc_position, 2519 "for " + text + " offset " + offset); 2520 2521 offset = text_width - step; 2522 adjusted_offset = alignOffset(offset, text_width); 2523 tm_position = tm.getIndexFromOffset(adjusted_offset); 2524 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2525 assert_equals(tm_position, 2526 doc_position, 2527 "for " + text + " offset " + offset); 2528 } 2529 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 0px letter spacing and directional-override."); 2530 2531 test(t => { 2532 const canvas = document.createElement('canvas'); 2533 canvas.width = 100; 2534 canvas.height = 50; 2535 const ctx = canvas.getContext('2d'); 2536 2537 function alignOffset(offset, width) { 2538 if ('center' == 'center') { 2539 offset -= width / 2; 2540 } else if ('center' == 'right' || 2541 ('rtl' == 'ltr' && 'center' == 'end') || 2542 ('rtl' == 'rtl' && 'center' == 'start')) { 2543 offset -= width; 2544 } 2545 return offset; 2546 } 2547 2548 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 2549 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 2550 const LTR_OVERRIDE = '\u202D'; 2551 const RTL_OVERRIDE = '\u202E'; 2552 const OVERRIDE_END = '\u202C'; 2553 if (direction_is_ltr) { 2554 return LTR_OVERRIDE + text + OVERRIDE_END; 2555 } 2556 return RTL_OVERRIDE + text + OVERRIDE_END; 2557 } 2558 2559 function placeAndMeasureTextInDOM(text, text_width, point) { 2560 const el = document.createElement("p"); 2561 el.innerHTML = text; 2562 el.style.font = '50px sans-serif'; 2563 el.style.direction = 'rtl'; 2564 el.style.letterSpacing = '0px'; 2565 // Put the text top left to make offsets simpler. 2566 el.style.padding = '0px'; 2567 el.style.margin = '0px'; 2568 el.style.position = 'absolute'; 2569 el.style.x = '0px'; 2570 el.style.y = '0px'; 2571 document.body.appendChild(el); 2572 text_bound = el.getBoundingClientRect(); 2573 text_x = text_bound.x; 2574 text_y = text_bound.y + text_bound.height / 2; 2575 2576 // Offset to the requested point determined by textAlign and direction. 2577 let text_align_dx = 0; 2578 if ('center' == 'center') { 2579 text_align_dx = text_width / 2; 2580 } else if ('center' == 'right' || 2581 ('rtl' == 'ltr' && 'center' == 'end') || 2582 ('rtl' == 'rtl' && 'center' == 'start')) { 2583 text_align_dx = text_width; 2584 } 2585 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2586 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2587 2588 document.body.removeChild(el); 2589 2590 return position.offset; 2591 } 2592 2593 ctx.font = '50px sans-serif'; 2594 ctx.direction = 'rtl'; 2595 ctx.textAlign = 'center'; 2596 ctx.letterSpacing = '0px'; 2597 2598 const kTexts = [ 2599 'UNAVAILABLE', 2600 'ππΆπ', 2601 'οΌοΌγοΌοΌ', 2602 'οΌοΌγγοΌοΌ', 2603 'γγοΌοΌγγ', 2604 '--abcd__' 2605 ] 2606 2607 for (text of kTexts) { 2608 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 2609 const tm = ctx.measureText(text); 2610 text_width = tm.width; 2611 step = 30; 2612 if ('0px' == '10px') { 2613 step = 40; 2614 } 2615 2616 offset = step; 2617 adjusted_offset = alignOffset(offset, text_width); 2618 tm_position = tm.getIndexFromOffset(adjusted_offset); 2619 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2620 assert_equals(tm_position, 2621 doc_position, 2622 "for " + text + " offset " + offset); 2623 2624 offset = text_width / 2 - 10; 2625 adjusted_offset = alignOffset(offset, text_width); 2626 tm_position = tm.getIndexFromOffset(adjusted_offset); 2627 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2628 assert_equals(tm_position, 2629 doc_position, 2630 "for " + text + " offset " + offset); 2631 2632 offset = text_width / 2 + 10; 2633 adjusted_offset = alignOffset(offset, text_width); 2634 tm_position = tm.getIndexFromOffset(adjusted_offset); 2635 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2636 assert_equals(tm_position, 2637 doc_position, 2638 "for " + text + " offset " + offset); 2639 2640 offset = text_width - step; 2641 adjusted_offset = alignOffset(offset, text_width); 2642 tm_position = tm.getIndexFromOffset(adjusted_offset); 2643 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2644 assert_equals(tm_position, 2645 doc_position, 2646 "for " + text + " offset " + offset); 2647 } 2648 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 0px letter spacing and directional-override."); 2649 2650 test(t => { 2651 const canvas = document.createElement('canvas'); 2652 canvas.width = 100; 2653 canvas.height = 50; 2654 const ctx = canvas.getContext('2d'); 2655 2656 function alignOffset(offset, width) { 2657 if ('right' == 'center') { 2658 offset -= width / 2; 2659 } else if ('right' == 'right' || 2660 ('ltr' == 'ltr' && 'right' == 'end') || 2661 ('ltr' == 'rtl' && 'right' == 'start')) { 2662 offset -= width; 2663 } 2664 return offset; 2665 } 2666 2667 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 2668 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 2669 const LTR_OVERRIDE = '\u202D'; 2670 const RTL_OVERRIDE = '\u202E'; 2671 const OVERRIDE_END = '\u202C'; 2672 if (direction_is_ltr) { 2673 return LTR_OVERRIDE + text + OVERRIDE_END; 2674 } 2675 return RTL_OVERRIDE + text + OVERRIDE_END; 2676 } 2677 2678 function placeAndMeasureTextInDOM(text, text_width, point) { 2679 const el = document.createElement("p"); 2680 el.innerHTML = text; 2681 el.style.font = '50px sans-serif'; 2682 el.style.direction = 'ltr'; 2683 el.style.letterSpacing = '0px'; 2684 // Put the text top left to make offsets simpler. 2685 el.style.padding = '0px'; 2686 el.style.margin = '0px'; 2687 el.style.position = 'absolute'; 2688 el.style.x = '0px'; 2689 el.style.y = '0px'; 2690 document.body.appendChild(el); 2691 text_bound = el.getBoundingClientRect(); 2692 text_x = text_bound.x; 2693 text_y = text_bound.y + text_bound.height / 2; 2694 2695 // Offset to the requested point determined by textAlign and direction. 2696 let text_align_dx = 0; 2697 if ('right' == 'center') { 2698 text_align_dx = text_width / 2; 2699 } else if ('right' == 'right' || 2700 ('ltr' == 'ltr' && 'right' == 'end') || 2701 ('ltr' == 'rtl' && 'right' == 'start')) { 2702 text_align_dx = text_width; 2703 } 2704 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2705 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2706 2707 document.body.removeChild(el); 2708 2709 return position.offset; 2710 } 2711 2712 ctx.font = '50px sans-serif'; 2713 ctx.direction = 'ltr'; 2714 ctx.textAlign = 'right'; 2715 ctx.letterSpacing = '0px'; 2716 2717 const kTexts = [ 2718 'UNAVAILABLE', 2719 'ππΆπ', 2720 'οΌοΌγοΌοΌ', 2721 'οΌοΌγγοΌοΌ', 2722 'γγοΌοΌγγ', 2723 '--abcd__' 2724 ] 2725 2726 for (text of kTexts) { 2727 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 2728 const tm = ctx.measureText(text); 2729 text_width = tm.width; 2730 step = 30; 2731 if ('0px' == '10px') { 2732 step = 40; 2733 } 2734 2735 offset = step; 2736 adjusted_offset = alignOffset(offset, text_width); 2737 tm_position = tm.getIndexFromOffset(adjusted_offset); 2738 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2739 assert_equals(tm_position, 2740 doc_position, 2741 "for " + text + " offset " + offset); 2742 2743 offset = text_width / 2 - 10; 2744 adjusted_offset = alignOffset(offset, text_width); 2745 tm_position = tm.getIndexFromOffset(adjusted_offset); 2746 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2747 assert_equals(tm_position, 2748 doc_position, 2749 "for " + text + " offset " + offset); 2750 2751 offset = text_width / 2 + 10; 2752 adjusted_offset = alignOffset(offset, text_width); 2753 tm_position = tm.getIndexFromOffset(adjusted_offset); 2754 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2755 assert_equals(tm_position, 2756 doc_position, 2757 "for " + text + " offset " + offset); 2758 2759 offset = text_width - step; 2760 adjusted_offset = alignOffset(offset, text_width); 2761 tm_position = tm.getIndexFromOffset(adjusted_offset); 2762 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2763 assert_equals(tm_position, 2764 doc_position, 2765 "for " + text + " offset " + offset); 2766 } 2767 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 0px letter spacing and directional-override."); 2768 2769 test(t => { 2770 const canvas = document.createElement('canvas'); 2771 canvas.width = 100; 2772 canvas.height = 50; 2773 const ctx = canvas.getContext('2d'); 2774 2775 function alignOffset(offset, width) { 2776 if ('right' == 'center') { 2777 offset -= width / 2; 2778 } else if ('right' == 'right' || 2779 ('rtl' == 'ltr' && 'right' == 'end') || 2780 ('rtl' == 'rtl' && 'right' == 'start')) { 2781 offset -= width; 2782 } 2783 return offset; 2784 } 2785 2786 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 2787 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 2788 const LTR_OVERRIDE = '\u202D'; 2789 const RTL_OVERRIDE = '\u202E'; 2790 const OVERRIDE_END = '\u202C'; 2791 if (direction_is_ltr) { 2792 return LTR_OVERRIDE + text + OVERRIDE_END; 2793 } 2794 return RTL_OVERRIDE + text + OVERRIDE_END; 2795 } 2796 2797 function placeAndMeasureTextInDOM(text, text_width, point) { 2798 const el = document.createElement("p"); 2799 el.innerHTML = text; 2800 el.style.font = '50px sans-serif'; 2801 el.style.direction = 'rtl'; 2802 el.style.letterSpacing = '0px'; 2803 // Put the text top left to make offsets simpler. 2804 el.style.padding = '0px'; 2805 el.style.margin = '0px'; 2806 el.style.position = 'absolute'; 2807 el.style.x = '0px'; 2808 el.style.y = '0px'; 2809 document.body.appendChild(el); 2810 text_bound = el.getBoundingClientRect(); 2811 text_x = text_bound.x; 2812 text_y = text_bound.y + text_bound.height / 2; 2813 2814 // Offset to the requested point determined by textAlign and direction. 2815 let text_align_dx = 0; 2816 if ('right' == 'center') { 2817 text_align_dx = text_width / 2; 2818 } else if ('right' == 'right' || 2819 ('rtl' == 'ltr' && 'right' == 'end') || 2820 ('rtl' == 'rtl' && 'right' == 'start')) { 2821 text_align_dx = text_width; 2822 } 2823 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2824 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2825 2826 document.body.removeChild(el); 2827 2828 return position.offset; 2829 } 2830 2831 ctx.font = '50px sans-serif'; 2832 ctx.direction = 'rtl'; 2833 ctx.textAlign = 'right'; 2834 ctx.letterSpacing = '0px'; 2835 2836 const kTexts = [ 2837 'UNAVAILABLE', 2838 'ππΆπ', 2839 'οΌοΌγοΌοΌ', 2840 'οΌοΌγγοΌοΌ', 2841 'γγοΌοΌγγ', 2842 '--abcd__' 2843 ] 2844 2845 for (text of kTexts) { 2846 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 2847 const tm = ctx.measureText(text); 2848 text_width = tm.width; 2849 step = 30; 2850 if ('0px' == '10px') { 2851 step = 40; 2852 } 2853 2854 offset = step; 2855 adjusted_offset = alignOffset(offset, text_width); 2856 tm_position = tm.getIndexFromOffset(adjusted_offset); 2857 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2858 assert_equals(tm_position, 2859 doc_position, 2860 "for " + text + " offset " + offset); 2861 2862 offset = text_width / 2 - 10; 2863 adjusted_offset = alignOffset(offset, text_width); 2864 tm_position = tm.getIndexFromOffset(adjusted_offset); 2865 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2866 assert_equals(tm_position, 2867 doc_position, 2868 "for " + text + " offset " + offset); 2869 2870 offset = text_width / 2 + 10; 2871 adjusted_offset = alignOffset(offset, text_width); 2872 tm_position = tm.getIndexFromOffset(adjusted_offset); 2873 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2874 assert_equals(tm_position, 2875 doc_position, 2876 "for " + text + " offset " + offset); 2877 2878 offset = text_width - step; 2879 adjusted_offset = alignOffset(offset, text_width); 2880 tm_position = tm.getIndexFromOffset(adjusted_offset); 2881 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2882 assert_equals(tm_position, 2883 doc_position, 2884 "for " + text + " offset " + offset); 2885 } 2886 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 0px letter spacing and directional-override."); 2887 2888 test(t => { 2889 const canvas = document.createElement('canvas'); 2890 canvas.width = 100; 2891 canvas.height = 50; 2892 const ctx = canvas.getContext('2d'); 2893 2894 function alignOffset(offset, width) { 2895 if ('start' == 'center') { 2896 offset -= width / 2; 2897 } else if ('start' == 'right' || 2898 ('ltr' == 'ltr' && 'start' == 'end') || 2899 ('ltr' == 'rtl' && 'start' == 'start')) { 2900 offset -= width; 2901 } 2902 return offset; 2903 } 2904 2905 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 2906 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 2907 const LTR_OVERRIDE = '\u202D'; 2908 const RTL_OVERRIDE = '\u202E'; 2909 const OVERRIDE_END = '\u202C'; 2910 if (direction_is_ltr) { 2911 return LTR_OVERRIDE + text + OVERRIDE_END; 2912 } 2913 return RTL_OVERRIDE + text + OVERRIDE_END; 2914 } 2915 2916 function placeAndMeasureTextInDOM(text, text_width, point) { 2917 const el = document.createElement("p"); 2918 el.innerHTML = text; 2919 el.style.font = '50px sans-serif'; 2920 el.style.direction = 'ltr'; 2921 el.style.letterSpacing = '0px'; 2922 // Put the text top left to make offsets simpler. 2923 el.style.padding = '0px'; 2924 el.style.margin = '0px'; 2925 el.style.position = 'absolute'; 2926 el.style.x = '0px'; 2927 el.style.y = '0px'; 2928 document.body.appendChild(el); 2929 text_bound = el.getBoundingClientRect(); 2930 text_x = text_bound.x; 2931 text_y = text_bound.y + text_bound.height / 2; 2932 2933 // Offset to the requested point determined by textAlign and direction. 2934 let text_align_dx = 0; 2935 if ('start' == 'center') { 2936 text_align_dx = text_width / 2; 2937 } else if ('start' == 'right' || 2938 ('ltr' == 'ltr' && 'start' == 'end') || 2939 ('ltr' == 'rtl' && 'start' == 'start')) { 2940 text_align_dx = text_width; 2941 } 2942 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 2943 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 2944 2945 document.body.removeChild(el); 2946 2947 return position.offset; 2948 } 2949 2950 ctx.font = '50px sans-serif'; 2951 ctx.direction = 'ltr'; 2952 ctx.textAlign = 'start'; 2953 ctx.letterSpacing = '0px'; 2954 2955 const kTexts = [ 2956 'UNAVAILABLE', 2957 'ππΆπ', 2958 'οΌοΌγοΌοΌ', 2959 'οΌοΌγγοΌοΌ', 2960 'γγοΌοΌγγ', 2961 '--abcd__' 2962 ] 2963 2964 for (text of kTexts) { 2965 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 2966 const tm = ctx.measureText(text); 2967 text_width = tm.width; 2968 step = 30; 2969 if ('0px' == '10px') { 2970 step = 40; 2971 } 2972 2973 offset = step; 2974 adjusted_offset = alignOffset(offset, text_width); 2975 tm_position = tm.getIndexFromOffset(adjusted_offset); 2976 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2977 assert_equals(tm_position, 2978 doc_position, 2979 "for " + text + " offset " + offset); 2980 2981 offset = text_width / 2 - 10; 2982 adjusted_offset = alignOffset(offset, text_width); 2983 tm_position = tm.getIndexFromOffset(adjusted_offset); 2984 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2985 assert_equals(tm_position, 2986 doc_position, 2987 "for " + text + " offset " + offset); 2988 2989 offset = text_width / 2 + 10; 2990 adjusted_offset = alignOffset(offset, text_width); 2991 tm_position = tm.getIndexFromOffset(adjusted_offset); 2992 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 2993 assert_equals(tm_position, 2994 doc_position, 2995 "for " + text + " offset " + offset); 2996 2997 offset = text_width - step; 2998 adjusted_offset = alignOffset(offset, text_width); 2999 tm_position = tm.getIndexFromOffset(adjusted_offset); 3000 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3001 assert_equals(tm_position, 3002 doc_position, 3003 "for " + text + " offset " + offset); 3004 } 3005 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 0px letter spacing and directional-override."); 3006 3007 test(t => { 3008 const canvas = document.createElement('canvas'); 3009 canvas.width = 100; 3010 canvas.height = 50; 3011 const ctx = canvas.getContext('2d'); 3012 3013 function alignOffset(offset, width) { 3014 if ('start' == 'center') { 3015 offset -= width / 2; 3016 } else if ('start' == 'right' || 3017 ('rtl' == 'ltr' && 'start' == 'end') || 3018 ('rtl' == 'rtl' && 'start' == 'start')) { 3019 offset -= width; 3020 } 3021 return offset; 3022 } 3023 3024 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3025 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3026 const LTR_OVERRIDE = '\u202D'; 3027 const RTL_OVERRIDE = '\u202E'; 3028 const OVERRIDE_END = '\u202C'; 3029 if (direction_is_ltr) { 3030 return LTR_OVERRIDE + text + OVERRIDE_END; 3031 } 3032 return RTL_OVERRIDE + text + OVERRIDE_END; 3033 } 3034 3035 function placeAndMeasureTextInDOM(text, text_width, point) { 3036 const el = document.createElement("p"); 3037 el.innerHTML = text; 3038 el.style.font = '50px sans-serif'; 3039 el.style.direction = 'rtl'; 3040 el.style.letterSpacing = '0px'; 3041 // Put the text top left to make offsets simpler. 3042 el.style.padding = '0px'; 3043 el.style.margin = '0px'; 3044 el.style.position = 'absolute'; 3045 el.style.x = '0px'; 3046 el.style.y = '0px'; 3047 document.body.appendChild(el); 3048 text_bound = el.getBoundingClientRect(); 3049 text_x = text_bound.x; 3050 text_y = text_bound.y + text_bound.height / 2; 3051 3052 // Offset to the requested point determined by textAlign and direction. 3053 let text_align_dx = 0; 3054 if ('start' == 'center') { 3055 text_align_dx = text_width / 2; 3056 } else if ('start' == 'right' || 3057 ('rtl' == 'ltr' && 'start' == 'end') || 3058 ('rtl' == 'rtl' && 'start' == 'start')) { 3059 text_align_dx = text_width; 3060 } 3061 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3062 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3063 3064 document.body.removeChild(el); 3065 3066 return position.offset; 3067 } 3068 3069 ctx.font = '50px sans-serif'; 3070 ctx.direction = 'rtl'; 3071 ctx.textAlign = 'start'; 3072 ctx.letterSpacing = '0px'; 3073 3074 const kTexts = [ 3075 'UNAVAILABLE', 3076 'ππΆπ', 3077 'οΌοΌγοΌοΌ', 3078 'οΌοΌγγοΌοΌ', 3079 'γγοΌοΌγγ', 3080 '--abcd__' 3081 ] 3082 3083 for (text of kTexts) { 3084 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3085 const tm = ctx.measureText(text); 3086 text_width = tm.width; 3087 step = 30; 3088 if ('0px' == '10px') { 3089 step = 40; 3090 } 3091 3092 offset = step; 3093 adjusted_offset = alignOffset(offset, text_width); 3094 tm_position = tm.getIndexFromOffset(adjusted_offset); 3095 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3096 assert_equals(tm_position, 3097 doc_position, 3098 "for " + text + " offset " + offset); 3099 3100 offset = text_width / 2 - 10; 3101 adjusted_offset = alignOffset(offset, text_width); 3102 tm_position = tm.getIndexFromOffset(adjusted_offset); 3103 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3104 assert_equals(tm_position, 3105 doc_position, 3106 "for " + text + " offset " + offset); 3107 3108 offset = text_width / 2 + 10; 3109 adjusted_offset = alignOffset(offset, text_width); 3110 tm_position = tm.getIndexFromOffset(adjusted_offset); 3111 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3112 assert_equals(tm_position, 3113 doc_position, 3114 "for " + text + " offset " + offset); 3115 3116 offset = text_width - step; 3117 adjusted_offset = alignOffset(offset, text_width); 3118 tm_position = tm.getIndexFromOffset(adjusted_offset); 3119 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3120 assert_equals(tm_position, 3121 doc_position, 3122 "for " + text + " offset " + offset); 3123 } 3124 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 0px letter spacing and directional-override."); 3125 3126 test(t => { 3127 const canvas = document.createElement('canvas'); 3128 canvas.width = 100; 3129 canvas.height = 50; 3130 const ctx = canvas.getContext('2d'); 3131 3132 function alignOffset(offset, width) { 3133 if ('end' == 'center') { 3134 offset -= width / 2; 3135 } else if ('end' == 'right' || 3136 ('ltr' == 'ltr' && 'end' == 'end') || 3137 ('ltr' == 'rtl' && 'end' == 'start')) { 3138 offset -= width; 3139 } 3140 return offset; 3141 } 3142 3143 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3144 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3145 const LTR_OVERRIDE = '\u202D'; 3146 const RTL_OVERRIDE = '\u202E'; 3147 const OVERRIDE_END = '\u202C'; 3148 if (direction_is_ltr) { 3149 return LTR_OVERRIDE + text + OVERRIDE_END; 3150 } 3151 return RTL_OVERRIDE + text + OVERRIDE_END; 3152 } 3153 3154 function placeAndMeasureTextInDOM(text, text_width, point) { 3155 const el = document.createElement("p"); 3156 el.innerHTML = text; 3157 el.style.font = '50px sans-serif'; 3158 el.style.direction = 'ltr'; 3159 el.style.letterSpacing = '0px'; 3160 // Put the text top left to make offsets simpler. 3161 el.style.padding = '0px'; 3162 el.style.margin = '0px'; 3163 el.style.position = 'absolute'; 3164 el.style.x = '0px'; 3165 el.style.y = '0px'; 3166 document.body.appendChild(el); 3167 text_bound = el.getBoundingClientRect(); 3168 text_x = text_bound.x; 3169 text_y = text_bound.y + text_bound.height / 2; 3170 3171 // Offset to the requested point determined by textAlign and direction. 3172 let text_align_dx = 0; 3173 if ('end' == 'center') { 3174 text_align_dx = text_width / 2; 3175 } else if ('end' == 'right' || 3176 ('ltr' == 'ltr' && 'end' == 'end') || 3177 ('ltr' == 'rtl' && 'end' == 'start')) { 3178 text_align_dx = text_width; 3179 } 3180 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3181 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3182 3183 document.body.removeChild(el); 3184 3185 return position.offset; 3186 } 3187 3188 ctx.font = '50px sans-serif'; 3189 ctx.direction = 'ltr'; 3190 ctx.textAlign = 'end'; 3191 ctx.letterSpacing = '0px'; 3192 3193 const kTexts = [ 3194 'UNAVAILABLE', 3195 'ππΆπ', 3196 'οΌοΌγοΌοΌ', 3197 'οΌοΌγγοΌοΌ', 3198 'γγοΌοΌγγ', 3199 '--abcd__' 3200 ] 3201 3202 for (text of kTexts) { 3203 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3204 const tm = ctx.measureText(text); 3205 text_width = tm.width; 3206 step = 30; 3207 if ('0px' == '10px') { 3208 step = 40; 3209 } 3210 3211 offset = step; 3212 adjusted_offset = alignOffset(offset, text_width); 3213 tm_position = tm.getIndexFromOffset(adjusted_offset); 3214 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3215 assert_equals(tm_position, 3216 doc_position, 3217 "for " + text + " offset " + offset); 3218 3219 offset = text_width / 2 - 10; 3220 adjusted_offset = alignOffset(offset, text_width); 3221 tm_position = tm.getIndexFromOffset(adjusted_offset); 3222 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3223 assert_equals(tm_position, 3224 doc_position, 3225 "for " + text + " offset " + offset); 3226 3227 offset = text_width / 2 + 10; 3228 adjusted_offset = alignOffset(offset, text_width); 3229 tm_position = tm.getIndexFromOffset(adjusted_offset); 3230 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3231 assert_equals(tm_position, 3232 doc_position, 3233 "for " + text + " offset " + offset); 3234 3235 offset = text_width - step; 3236 adjusted_offset = alignOffset(offset, text_width); 3237 tm_position = tm.getIndexFromOffset(adjusted_offset); 3238 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3239 assert_equals(tm_position, 3240 doc_position, 3241 "for " + text + " offset " + offset); 3242 } 3243 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 0px letter spacing and directional-override."); 3244 3245 test(t => { 3246 const canvas = document.createElement('canvas'); 3247 canvas.width = 100; 3248 canvas.height = 50; 3249 const ctx = canvas.getContext('2d'); 3250 3251 function alignOffset(offset, width) { 3252 if ('end' == 'center') { 3253 offset -= width / 2; 3254 } else if ('end' == 'right' || 3255 ('rtl' == 'ltr' && 'end' == 'end') || 3256 ('rtl' == 'rtl' && 'end' == 'start')) { 3257 offset -= width; 3258 } 3259 return offset; 3260 } 3261 3262 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3263 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3264 const LTR_OVERRIDE = '\u202D'; 3265 const RTL_OVERRIDE = '\u202E'; 3266 const OVERRIDE_END = '\u202C'; 3267 if (direction_is_ltr) { 3268 return LTR_OVERRIDE + text + OVERRIDE_END; 3269 } 3270 return RTL_OVERRIDE + text + OVERRIDE_END; 3271 } 3272 3273 function placeAndMeasureTextInDOM(text, text_width, point) { 3274 const el = document.createElement("p"); 3275 el.innerHTML = text; 3276 el.style.font = '50px sans-serif'; 3277 el.style.direction = 'rtl'; 3278 el.style.letterSpacing = '0px'; 3279 // Put the text top left to make offsets simpler. 3280 el.style.padding = '0px'; 3281 el.style.margin = '0px'; 3282 el.style.position = 'absolute'; 3283 el.style.x = '0px'; 3284 el.style.y = '0px'; 3285 document.body.appendChild(el); 3286 text_bound = el.getBoundingClientRect(); 3287 text_x = text_bound.x; 3288 text_y = text_bound.y + text_bound.height / 2; 3289 3290 // Offset to the requested point determined by textAlign and direction. 3291 let text_align_dx = 0; 3292 if ('end' == 'center') { 3293 text_align_dx = text_width / 2; 3294 } else if ('end' == 'right' || 3295 ('rtl' == 'ltr' && 'end' == 'end') || 3296 ('rtl' == 'rtl' && 'end' == 'start')) { 3297 text_align_dx = text_width; 3298 } 3299 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3300 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3301 3302 document.body.removeChild(el); 3303 3304 return position.offset; 3305 } 3306 3307 ctx.font = '50px sans-serif'; 3308 ctx.direction = 'rtl'; 3309 ctx.textAlign = 'end'; 3310 ctx.letterSpacing = '0px'; 3311 3312 const kTexts = [ 3313 'UNAVAILABLE', 3314 'ππΆπ', 3315 'οΌοΌγοΌοΌ', 3316 'οΌοΌγγοΌοΌ', 3317 'γγοΌοΌγγ', 3318 '--abcd__' 3319 ] 3320 3321 for (text of kTexts) { 3322 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3323 const tm = ctx.measureText(text); 3324 text_width = tm.width; 3325 step = 30; 3326 if ('0px' == '10px') { 3327 step = 40; 3328 } 3329 3330 offset = step; 3331 adjusted_offset = alignOffset(offset, text_width); 3332 tm_position = tm.getIndexFromOffset(adjusted_offset); 3333 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3334 assert_equals(tm_position, 3335 doc_position, 3336 "for " + text + " offset " + offset); 3337 3338 offset = text_width / 2 - 10; 3339 adjusted_offset = alignOffset(offset, text_width); 3340 tm_position = tm.getIndexFromOffset(adjusted_offset); 3341 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3342 assert_equals(tm_position, 3343 doc_position, 3344 "for " + text + " offset " + offset); 3345 3346 offset = text_width / 2 + 10; 3347 adjusted_offset = alignOffset(offset, text_width); 3348 tm_position = tm.getIndexFromOffset(adjusted_offset); 3349 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3350 assert_equals(tm_position, 3351 doc_position, 3352 "for " + text + " offset " + offset); 3353 3354 offset = text_width - step; 3355 adjusted_offset = alignOffset(offset, text_width); 3356 tm_position = tm.getIndexFromOffset(adjusted_offset); 3357 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3358 assert_equals(tm_position, 3359 doc_position, 3360 "for " + text + " offset " + offset); 3361 } 3362 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 0px letter spacing and directional-override."); 3363 3364 test(t => { 3365 const canvas = document.createElement('canvas'); 3366 canvas.width = 100; 3367 canvas.height = 50; 3368 const ctx = canvas.getContext('2d'); 3369 3370 function alignOffset(offset, width) { 3371 if ('left' == 'center') { 3372 offset -= width / 2; 3373 } else if ('left' == 'right' || 3374 ('ltr' == 'ltr' && 'left' == 'end') || 3375 ('ltr' == 'rtl' && 'left' == 'start')) { 3376 offset -= width; 3377 } 3378 return offset; 3379 } 3380 3381 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3382 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3383 const LTR_OVERRIDE = '\u202D'; 3384 const RTL_OVERRIDE = '\u202E'; 3385 const OVERRIDE_END = '\u202C'; 3386 if (direction_is_ltr) { 3387 return LTR_OVERRIDE + text + OVERRIDE_END; 3388 } 3389 return RTL_OVERRIDE + text + OVERRIDE_END; 3390 } 3391 3392 function placeAndMeasureTextInDOM(text, text_width, point) { 3393 const el = document.createElement("p"); 3394 el.innerHTML = text; 3395 el.style.font = '50px sans-serif'; 3396 el.style.direction = 'ltr'; 3397 el.style.letterSpacing = '10px'; 3398 // Put the text top left to make offsets simpler. 3399 el.style.padding = '0px'; 3400 el.style.margin = '0px'; 3401 el.style.position = 'absolute'; 3402 el.style.x = '0px'; 3403 el.style.y = '0px'; 3404 document.body.appendChild(el); 3405 text_bound = el.getBoundingClientRect(); 3406 text_x = text_bound.x; 3407 text_y = text_bound.y + text_bound.height / 2; 3408 3409 // Offset to the requested point determined by textAlign and direction. 3410 let text_align_dx = 0; 3411 if ('left' == 'center') { 3412 text_align_dx = text_width / 2; 3413 } else if ('left' == 'right' || 3414 ('ltr' == 'ltr' && 'left' == 'end') || 3415 ('ltr' == 'rtl' && 'left' == 'start')) { 3416 text_align_dx = text_width; 3417 } 3418 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3419 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3420 3421 document.body.removeChild(el); 3422 3423 return position.offset; 3424 } 3425 3426 ctx.font = '50px sans-serif'; 3427 ctx.direction = 'ltr'; 3428 ctx.textAlign = 'left'; 3429 ctx.letterSpacing = '10px'; 3430 3431 const kTexts = [ 3432 'UNAVAILABLE', 3433 'ππΆπ', 3434 'οΌοΌγοΌοΌ', 3435 'οΌοΌγγοΌοΌ', 3436 'γγοΌοΌγγ', 3437 '--abcd__' 3438 ] 3439 3440 for (text of kTexts) { 3441 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3442 const tm = ctx.measureText(text); 3443 text_width = tm.width; 3444 step = 30; 3445 if ('10px' == '10px') { 3446 step = 40; 3447 } 3448 3449 offset = step; 3450 adjusted_offset = alignOffset(offset, text_width); 3451 tm_position = tm.getIndexFromOffset(adjusted_offset); 3452 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3453 assert_equals(tm_position, 3454 doc_position, 3455 "for " + text + " offset " + offset); 3456 3457 offset = text_width / 2 - 10; 3458 adjusted_offset = alignOffset(offset, text_width); 3459 tm_position = tm.getIndexFromOffset(adjusted_offset); 3460 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3461 assert_equals(tm_position, 3462 doc_position, 3463 "for " + text + " offset " + offset); 3464 3465 offset = text_width / 2 + 10; 3466 adjusted_offset = alignOffset(offset, text_width); 3467 tm_position = tm.getIndexFromOffset(adjusted_offset); 3468 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3469 assert_equals(tm_position, 3470 doc_position, 3471 "for " + text + " offset " + offset); 3472 3473 offset = text_width - step; 3474 adjusted_offset = alignOffset(offset, text_width); 3475 tm_position = tm.getIndexFromOffset(adjusted_offset); 3476 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3477 assert_equals(tm_position, 3478 doc_position, 3479 "for " + text + " offset " + offset); 3480 } 3481 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align left, 10px letter spacing and directional-override."); 3482 3483 test(t => { 3484 const canvas = document.createElement('canvas'); 3485 canvas.width = 100; 3486 canvas.height = 50; 3487 const ctx = canvas.getContext('2d'); 3488 3489 function alignOffset(offset, width) { 3490 if ('left' == 'center') { 3491 offset -= width / 2; 3492 } else if ('left' == 'right' || 3493 ('rtl' == 'ltr' && 'left' == 'end') || 3494 ('rtl' == 'rtl' && 'left' == 'start')) { 3495 offset -= width; 3496 } 3497 return offset; 3498 } 3499 3500 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3501 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3502 const LTR_OVERRIDE = '\u202D'; 3503 const RTL_OVERRIDE = '\u202E'; 3504 const OVERRIDE_END = '\u202C'; 3505 if (direction_is_ltr) { 3506 return LTR_OVERRIDE + text + OVERRIDE_END; 3507 } 3508 return RTL_OVERRIDE + text + OVERRIDE_END; 3509 } 3510 3511 function placeAndMeasureTextInDOM(text, text_width, point) { 3512 const el = document.createElement("p"); 3513 el.innerHTML = text; 3514 el.style.font = '50px sans-serif'; 3515 el.style.direction = 'rtl'; 3516 el.style.letterSpacing = '10px'; 3517 // Put the text top left to make offsets simpler. 3518 el.style.padding = '0px'; 3519 el.style.margin = '0px'; 3520 el.style.position = 'absolute'; 3521 el.style.x = '0px'; 3522 el.style.y = '0px'; 3523 document.body.appendChild(el); 3524 text_bound = el.getBoundingClientRect(); 3525 text_x = text_bound.x; 3526 text_y = text_bound.y + text_bound.height / 2; 3527 3528 // Offset to the requested point determined by textAlign and direction. 3529 let text_align_dx = 0; 3530 if ('left' == 'center') { 3531 text_align_dx = text_width / 2; 3532 } else if ('left' == 'right' || 3533 ('rtl' == 'ltr' && 'left' == 'end') || 3534 ('rtl' == 'rtl' && 'left' == 'start')) { 3535 text_align_dx = text_width; 3536 } 3537 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3538 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3539 3540 document.body.removeChild(el); 3541 3542 return position.offset; 3543 } 3544 3545 ctx.font = '50px sans-serif'; 3546 ctx.direction = 'rtl'; 3547 ctx.textAlign = 'left'; 3548 ctx.letterSpacing = '10px'; 3549 3550 const kTexts = [ 3551 'UNAVAILABLE', 3552 'ππΆπ', 3553 'οΌοΌγοΌοΌ', 3554 'οΌοΌγγοΌοΌ', 3555 'γγοΌοΌγγ', 3556 '--abcd__' 3557 ] 3558 3559 for (text of kTexts) { 3560 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3561 const tm = ctx.measureText(text); 3562 text_width = tm.width; 3563 step = 30; 3564 if ('10px' == '10px') { 3565 step = 40; 3566 } 3567 3568 offset = step; 3569 adjusted_offset = alignOffset(offset, text_width); 3570 tm_position = tm.getIndexFromOffset(adjusted_offset); 3571 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3572 assert_equals(tm_position, 3573 doc_position, 3574 "for " + text + " offset " + offset); 3575 3576 offset = text_width / 2 - 10; 3577 adjusted_offset = alignOffset(offset, text_width); 3578 tm_position = tm.getIndexFromOffset(adjusted_offset); 3579 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3580 assert_equals(tm_position, 3581 doc_position, 3582 "for " + text + " offset " + offset); 3583 3584 offset = text_width / 2 + 10; 3585 adjusted_offset = alignOffset(offset, text_width); 3586 tm_position = tm.getIndexFromOffset(adjusted_offset); 3587 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3588 assert_equals(tm_position, 3589 doc_position, 3590 "for " + text + " offset " + offset); 3591 3592 offset = text_width - step; 3593 adjusted_offset = alignOffset(offset, text_width); 3594 tm_position = tm.getIndexFromOffset(adjusted_offset); 3595 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3596 assert_equals(tm_position, 3597 doc_position, 3598 "for " + text + " offset " + offset); 3599 } 3600 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align left, 10px letter spacing and directional-override."); 3601 3602 test(t => { 3603 const canvas = document.createElement('canvas'); 3604 canvas.width = 100; 3605 canvas.height = 50; 3606 const ctx = canvas.getContext('2d'); 3607 3608 function alignOffset(offset, width) { 3609 if ('center' == 'center') { 3610 offset -= width / 2; 3611 } else if ('center' == 'right' || 3612 ('ltr' == 'ltr' && 'center' == 'end') || 3613 ('ltr' == 'rtl' && 'center' == 'start')) { 3614 offset -= width; 3615 } 3616 return offset; 3617 } 3618 3619 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3620 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3621 const LTR_OVERRIDE = '\u202D'; 3622 const RTL_OVERRIDE = '\u202E'; 3623 const OVERRIDE_END = '\u202C'; 3624 if (direction_is_ltr) { 3625 return LTR_OVERRIDE + text + OVERRIDE_END; 3626 } 3627 return RTL_OVERRIDE + text + OVERRIDE_END; 3628 } 3629 3630 function placeAndMeasureTextInDOM(text, text_width, point) { 3631 const el = document.createElement("p"); 3632 el.innerHTML = text; 3633 el.style.font = '50px sans-serif'; 3634 el.style.direction = 'ltr'; 3635 el.style.letterSpacing = '10px'; 3636 // Put the text top left to make offsets simpler. 3637 el.style.padding = '0px'; 3638 el.style.margin = '0px'; 3639 el.style.position = 'absolute'; 3640 el.style.x = '0px'; 3641 el.style.y = '0px'; 3642 document.body.appendChild(el); 3643 text_bound = el.getBoundingClientRect(); 3644 text_x = text_bound.x; 3645 text_y = text_bound.y + text_bound.height / 2; 3646 3647 // Offset to the requested point determined by textAlign and direction. 3648 let text_align_dx = 0; 3649 if ('center' == 'center') { 3650 text_align_dx = text_width / 2; 3651 } else if ('center' == 'right' || 3652 ('ltr' == 'ltr' && 'center' == 'end') || 3653 ('ltr' == 'rtl' && 'center' == 'start')) { 3654 text_align_dx = text_width; 3655 } 3656 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3657 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3658 3659 document.body.removeChild(el); 3660 3661 return position.offset; 3662 } 3663 3664 ctx.font = '50px sans-serif'; 3665 ctx.direction = 'ltr'; 3666 ctx.textAlign = 'center'; 3667 ctx.letterSpacing = '10px'; 3668 3669 const kTexts = [ 3670 'UNAVAILABLE', 3671 'ππΆπ', 3672 'οΌοΌγοΌοΌ', 3673 'οΌοΌγγοΌοΌ', 3674 'γγοΌοΌγγ', 3675 '--abcd__' 3676 ] 3677 3678 for (text of kTexts) { 3679 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3680 const tm = ctx.measureText(text); 3681 text_width = tm.width; 3682 step = 30; 3683 if ('10px' == '10px') { 3684 step = 40; 3685 } 3686 3687 offset = step; 3688 adjusted_offset = alignOffset(offset, text_width); 3689 tm_position = tm.getIndexFromOffset(adjusted_offset); 3690 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3691 assert_equals(tm_position, 3692 doc_position, 3693 "for " + text + " offset " + offset); 3694 3695 offset = text_width / 2 - 10; 3696 adjusted_offset = alignOffset(offset, text_width); 3697 tm_position = tm.getIndexFromOffset(adjusted_offset); 3698 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3699 assert_equals(tm_position, 3700 doc_position, 3701 "for " + text + " offset " + offset); 3702 3703 offset = text_width / 2 + 10; 3704 adjusted_offset = alignOffset(offset, text_width); 3705 tm_position = tm.getIndexFromOffset(adjusted_offset); 3706 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3707 assert_equals(tm_position, 3708 doc_position, 3709 "for " + text + " offset " + offset); 3710 3711 offset = text_width - step; 3712 adjusted_offset = alignOffset(offset, text_width); 3713 tm_position = tm.getIndexFromOffset(adjusted_offset); 3714 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3715 assert_equals(tm_position, 3716 doc_position, 3717 "for " + text + " offset " + offset); 3718 } 3719 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align center, 10px letter spacing and directional-override."); 3720 3721 test(t => { 3722 const canvas = document.createElement('canvas'); 3723 canvas.width = 100; 3724 canvas.height = 50; 3725 const ctx = canvas.getContext('2d'); 3726 3727 function alignOffset(offset, width) { 3728 if ('center' == 'center') { 3729 offset -= width / 2; 3730 } else if ('center' == 'right' || 3731 ('rtl' == 'ltr' && 'center' == 'end') || 3732 ('rtl' == 'rtl' && 'center' == 'start')) { 3733 offset -= width; 3734 } 3735 return offset; 3736 } 3737 3738 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3739 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3740 const LTR_OVERRIDE = '\u202D'; 3741 const RTL_OVERRIDE = '\u202E'; 3742 const OVERRIDE_END = '\u202C'; 3743 if (direction_is_ltr) { 3744 return LTR_OVERRIDE + text + OVERRIDE_END; 3745 } 3746 return RTL_OVERRIDE + text + OVERRIDE_END; 3747 } 3748 3749 function placeAndMeasureTextInDOM(text, text_width, point) { 3750 const el = document.createElement("p"); 3751 el.innerHTML = text; 3752 el.style.font = '50px sans-serif'; 3753 el.style.direction = 'rtl'; 3754 el.style.letterSpacing = '10px'; 3755 // Put the text top left to make offsets simpler. 3756 el.style.padding = '0px'; 3757 el.style.margin = '0px'; 3758 el.style.position = 'absolute'; 3759 el.style.x = '0px'; 3760 el.style.y = '0px'; 3761 document.body.appendChild(el); 3762 text_bound = el.getBoundingClientRect(); 3763 text_x = text_bound.x; 3764 text_y = text_bound.y + text_bound.height / 2; 3765 3766 // Offset to the requested point determined by textAlign and direction. 3767 let text_align_dx = 0; 3768 if ('center' == 'center') { 3769 text_align_dx = text_width / 2; 3770 } else if ('center' == 'right' || 3771 ('rtl' == 'ltr' && 'center' == 'end') || 3772 ('rtl' == 'rtl' && 'center' == 'start')) { 3773 text_align_dx = text_width; 3774 } 3775 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3776 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3777 3778 document.body.removeChild(el); 3779 3780 return position.offset; 3781 } 3782 3783 ctx.font = '50px sans-serif'; 3784 ctx.direction = 'rtl'; 3785 ctx.textAlign = 'center'; 3786 ctx.letterSpacing = '10px'; 3787 3788 const kTexts = [ 3789 'UNAVAILABLE', 3790 'ππΆπ', 3791 'οΌοΌγοΌοΌ', 3792 'οΌοΌγγοΌοΌ', 3793 'γγοΌοΌγγ', 3794 '--abcd__' 3795 ] 3796 3797 for (text of kTexts) { 3798 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3799 const tm = ctx.measureText(text); 3800 text_width = tm.width; 3801 step = 30; 3802 if ('10px' == '10px') { 3803 step = 40; 3804 } 3805 3806 offset = step; 3807 adjusted_offset = alignOffset(offset, text_width); 3808 tm_position = tm.getIndexFromOffset(adjusted_offset); 3809 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3810 assert_equals(tm_position, 3811 doc_position, 3812 "for " + text + " offset " + offset); 3813 3814 offset = text_width / 2 - 10; 3815 adjusted_offset = alignOffset(offset, text_width); 3816 tm_position = tm.getIndexFromOffset(adjusted_offset); 3817 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3818 assert_equals(tm_position, 3819 doc_position, 3820 "for " + text + " offset " + offset); 3821 3822 offset = text_width / 2 + 10; 3823 adjusted_offset = alignOffset(offset, text_width); 3824 tm_position = tm.getIndexFromOffset(adjusted_offset); 3825 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3826 assert_equals(tm_position, 3827 doc_position, 3828 "for " + text + " offset " + offset); 3829 3830 offset = text_width - step; 3831 adjusted_offset = alignOffset(offset, text_width); 3832 tm_position = tm.getIndexFromOffset(adjusted_offset); 3833 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3834 assert_equals(tm_position, 3835 doc_position, 3836 "for " + text + " offset " + offset); 3837 } 3838 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align center, 10px letter spacing and directional-override."); 3839 3840 test(t => { 3841 const canvas = document.createElement('canvas'); 3842 canvas.width = 100; 3843 canvas.height = 50; 3844 const ctx = canvas.getContext('2d'); 3845 3846 function alignOffset(offset, width) { 3847 if ('right' == 'center') { 3848 offset -= width / 2; 3849 } else if ('right' == 'right' || 3850 ('ltr' == 'ltr' && 'right' == 'end') || 3851 ('ltr' == 'rtl' && 'right' == 'start')) { 3852 offset -= width; 3853 } 3854 return offset; 3855 } 3856 3857 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3858 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3859 const LTR_OVERRIDE = '\u202D'; 3860 const RTL_OVERRIDE = '\u202E'; 3861 const OVERRIDE_END = '\u202C'; 3862 if (direction_is_ltr) { 3863 return LTR_OVERRIDE + text + OVERRIDE_END; 3864 } 3865 return RTL_OVERRIDE + text + OVERRIDE_END; 3866 } 3867 3868 function placeAndMeasureTextInDOM(text, text_width, point) { 3869 const el = document.createElement("p"); 3870 el.innerHTML = text; 3871 el.style.font = '50px sans-serif'; 3872 el.style.direction = 'ltr'; 3873 el.style.letterSpacing = '10px'; 3874 // Put the text top left to make offsets simpler. 3875 el.style.padding = '0px'; 3876 el.style.margin = '0px'; 3877 el.style.position = 'absolute'; 3878 el.style.x = '0px'; 3879 el.style.y = '0px'; 3880 document.body.appendChild(el); 3881 text_bound = el.getBoundingClientRect(); 3882 text_x = text_bound.x; 3883 text_y = text_bound.y + text_bound.height / 2; 3884 3885 // Offset to the requested point determined by textAlign and direction. 3886 let text_align_dx = 0; 3887 if ('right' == 'center') { 3888 text_align_dx = text_width / 2; 3889 } else if ('right' == 'right' || 3890 ('ltr' == 'ltr' && 'right' == 'end') || 3891 ('ltr' == 'rtl' && 'right' == 'start')) { 3892 text_align_dx = text_width; 3893 } 3894 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 3895 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 3896 3897 document.body.removeChild(el); 3898 3899 return position.offset; 3900 } 3901 3902 ctx.font = '50px sans-serif'; 3903 ctx.direction = 'ltr'; 3904 ctx.textAlign = 'right'; 3905 ctx.letterSpacing = '10px'; 3906 3907 const kTexts = [ 3908 'UNAVAILABLE', 3909 'ππΆπ', 3910 'οΌοΌγοΌοΌ', 3911 'οΌοΌγγοΌοΌ', 3912 'γγοΌοΌγγ', 3913 '--abcd__' 3914 ] 3915 3916 for (text of kTexts) { 3917 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 3918 const tm = ctx.measureText(text); 3919 text_width = tm.width; 3920 step = 30; 3921 if ('10px' == '10px') { 3922 step = 40; 3923 } 3924 3925 offset = step; 3926 adjusted_offset = alignOffset(offset, text_width); 3927 tm_position = tm.getIndexFromOffset(adjusted_offset); 3928 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3929 assert_equals(tm_position, 3930 doc_position, 3931 "for " + text + " offset " + offset); 3932 3933 offset = text_width / 2 - 10; 3934 adjusted_offset = alignOffset(offset, text_width); 3935 tm_position = tm.getIndexFromOffset(adjusted_offset); 3936 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3937 assert_equals(tm_position, 3938 doc_position, 3939 "for " + text + " offset " + offset); 3940 3941 offset = text_width / 2 + 10; 3942 adjusted_offset = alignOffset(offset, text_width); 3943 tm_position = tm.getIndexFromOffset(adjusted_offset); 3944 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3945 assert_equals(tm_position, 3946 doc_position, 3947 "for " + text + " offset " + offset); 3948 3949 offset = text_width - step; 3950 adjusted_offset = alignOffset(offset, text_width); 3951 tm_position = tm.getIndexFromOffset(adjusted_offset); 3952 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 3953 assert_equals(tm_position, 3954 doc_position, 3955 "for " + text + " offset " + offset); 3956 } 3957 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align right, 10px letter spacing and directional-override."); 3958 3959 test(t => { 3960 const canvas = document.createElement('canvas'); 3961 canvas.width = 100; 3962 canvas.height = 50; 3963 const ctx = canvas.getContext('2d'); 3964 3965 function alignOffset(offset, width) { 3966 if ('right' == 'center') { 3967 offset -= width / 2; 3968 } else if ('right' == 'right' || 3969 ('rtl' == 'ltr' && 'right' == 'end') || 3970 ('rtl' == 'rtl' && 'right' == 'start')) { 3971 offset -= width; 3972 } 3973 return offset; 3974 } 3975 3976 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 3977 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 3978 const LTR_OVERRIDE = '\u202D'; 3979 const RTL_OVERRIDE = '\u202E'; 3980 const OVERRIDE_END = '\u202C'; 3981 if (direction_is_ltr) { 3982 return LTR_OVERRIDE + text + OVERRIDE_END; 3983 } 3984 return RTL_OVERRIDE + text + OVERRIDE_END; 3985 } 3986 3987 function placeAndMeasureTextInDOM(text, text_width, point) { 3988 const el = document.createElement("p"); 3989 el.innerHTML = text; 3990 el.style.font = '50px sans-serif'; 3991 el.style.direction = 'rtl'; 3992 el.style.letterSpacing = '10px'; 3993 // Put the text top left to make offsets simpler. 3994 el.style.padding = '0px'; 3995 el.style.margin = '0px'; 3996 el.style.position = 'absolute'; 3997 el.style.x = '0px'; 3998 el.style.y = '0px'; 3999 document.body.appendChild(el); 4000 text_bound = el.getBoundingClientRect(); 4001 text_x = text_bound.x; 4002 text_y = text_bound.y + text_bound.height / 2; 4003 4004 // Offset to the requested point determined by textAlign and direction. 4005 let text_align_dx = 0; 4006 if ('right' == 'center') { 4007 text_align_dx = text_width / 2; 4008 } else if ('right' == 'right' || 4009 ('rtl' == 'ltr' && 'right' == 'end') || 4010 ('rtl' == 'rtl' && 'right' == 'start')) { 4011 text_align_dx = text_width; 4012 } 4013 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 4014 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 4015 4016 document.body.removeChild(el); 4017 4018 return position.offset; 4019 } 4020 4021 ctx.font = '50px sans-serif'; 4022 ctx.direction = 'rtl'; 4023 ctx.textAlign = 'right'; 4024 ctx.letterSpacing = '10px'; 4025 4026 const kTexts = [ 4027 'UNAVAILABLE', 4028 'ππΆπ', 4029 'οΌοΌγοΌοΌ', 4030 'οΌοΌγγοΌοΌ', 4031 'γγοΌοΌγγ', 4032 '--abcd__' 4033 ] 4034 4035 for (text of kTexts) { 4036 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 4037 const tm = ctx.measureText(text); 4038 text_width = tm.width; 4039 step = 30; 4040 if ('10px' == '10px') { 4041 step = 40; 4042 } 4043 4044 offset = step; 4045 adjusted_offset = alignOffset(offset, text_width); 4046 tm_position = tm.getIndexFromOffset(adjusted_offset); 4047 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4048 assert_equals(tm_position, 4049 doc_position, 4050 "for " + text + " offset " + offset); 4051 4052 offset = text_width / 2 - 10; 4053 adjusted_offset = alignOffset(offset, text_width); 4054 tm_position = tm.getIndexFromOffset(adjusted_offset); 4055 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4056 assert_equals(tm_position, 4057 doc_position, 4058 "for " + text + " offset " + offset); 4059 4060 offset = text_width / 2 + 10; 4061 adjusted_offset = alignOffset(offset, text_width); 4062 tm_position = tm.getIndexFromOffset(adjusted_offset); 4063 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4064 assert_equals(tm_position, 4065 doc_position, 4066 "for " + text + " offset " + offset); 4067 4068 offset = text_width - step; 4069 adjusted_offset = alignOffset(offset, text_width); 4070 tm_position = tm.getIndexFromOffset(adjusted_offset); 4071 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4072 assert_equals(tm_position, 4073 doc_position, 4074 "for " + text + " offset " + offset); 4075 } 4076 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align right, 10px letter spacing and directional-override."); 4077 4078 test(t => { 4079 const canvas = document.createElement('canvas'); 4080 canvas.width = 100; 4081 canvas.height = 50; 4082 const ctx = canvas.getContext('2d'); 4083 4084 function alignOffset(offset, width) { 4085 if ('start' == 'center') { 4086 offset -= width / 2; 4087 } else if ('start' == 'right' || 4088 ('ltr' == 'ltr' && 'start' == 'end') || 4089 ('ltr' == 'rtl' && 'start' == 'start')) { 4090 offset -= width; 4091 } 4092 return offset; 4093 } 4094 4095 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 4096 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 4097 const LTR_OVERRIDE = '\u202D'; 4098 const RTL_OVERRIDE = '\u202E'; 4099 const OVERRIDE_END = '\u202C'; 4100 if (direction_is_ltr) { 4101 return LTR_OVERRIDE + text + OVERRIDE_END; 4102 } 4103 return RTL_OVERRIDE + text + OVERRIDE_END; 4104 } 4105 4106 function placeAndMeasureTextInDOM(text, text_width, point) { 4107 const el = document.createElement("p"); 4108 el.innerHTML = text; 4109 el.style.font = '50px sans-serif'; 4110 el.style.direction = 'ltr'; 4111 el.style.letterSpacing = '10px'; 4112 // Put the text top left to make offsets simpler. 4113 el.style.padding = '0px'; 4114 el.style.margin = '0px'; 4115 el.style.position = 'absolute'; 4116 el.style.x = '0px'; 4117 el.style.y = '0px'; 4118 document.body.appendChild(el); 4119 text_bound = el.getBoundingClientRect(); 4120 text_x = text_bound.x; 4121 text_y = text_bound.y + text_bound.height / 2; 4122 4123 // Offset to the requested point determined by textAlign and direction. 4124 let text_align_dx = 0; 4125 if ('start' == 'center') { 4126 text_align_dx = text_width / 2; 4127 } else if ('start' == 'right' || 4128 ('ltr' == 'ltr' && 'start' == 'end') || 4129 ('ltr' == 'rtl' && 'start' == 'start')) { 4130 text_align_dx = text_width; 4131 } 4132 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 4133 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 4134 4135 document.body.removeChild(el); 4136 4137 return position.offset; 4138 } 4139 4140 ctx.font = '50px sans-serif'; 4141 ctx.direction = 'ltr'; 4142 ctx.textAlign = 'start'; 4143 ctx.letterSpacing = '10px'; 4144 4145 const kTexts = [ 4146 'UNAVAILABLE', 4147 'ππΆπ', 4148 'οΌοΌγοΌοΌ', 4149 'οΌοΌγγοΌοΌ', 4150 'γγοΌοΌγγ', 4151 '--abcd__' 4152 ] 4153 4154 for (text of kTexts) { 4155 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 4156 const tm = ctx.measureText(text); 4157 text_width = tm.width; 4158 step = 30; 4159 if ('10px' == '10px') { 4160 step = 40; 4161 } 4162 4163 offset = step; 4164 adjusted_offset = alignOffset(offset, text_width); 4165 tm_position = tm.getIndexFromOffset(adjusted_offset); 4166 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4167 assert_equals(tm_position, 4168 doc_position, 4169 "for " + text + " offset " + offset); 4170 4171 offset = text_width / 2 - 10; 4172 adjusted_offset = alignOffset(offset, text_width); 4173 tm_position = tm.getIndexFromOffset(adjusted_offset); 4174 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4175 assert_equals(tm_position, 4176 doc_position, 4177 "for " + text + " offset " + offset); 4178 4179 offset = text_width / 2 + 10; 4180 adjusted_offset = alignOffset(offset, text_width); 4181 tm_position = tm.getIndexFromOffset(adjusted_offset); 4182 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4183 assert_equals(tm_position, 4184 doc_position, 4185 "for " + text + " offset " + offset); 4186 4187 offset = text_width - step; 4188 adjusted_offset = alignOffset(offset, text_width); 4189 tm_position = tm.getIndexFromOffset(adjusted_offset); 4190 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4191 assert_equals(tm_position, 4192 doc_position, 4193 "for " + text + " offset " + offset); 4194 } 4195 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align start, 10px letter spacing and directional-override."); 4196 4197 test(t => { 4198 const canvas = document.createElement('canvas'); 4199 canvas.width = 100; 4200 canvas.height = 50; 4201 const ctx = canvas.getContext('2d'); 4202 4203 function alignOffset(offset, width) { 4204 if ('start' == 'center') { 4205 offset -= width / 2; 4206 } else if ('start' == 'right' || 4207 ('rtl' == 'ltr' && 'start' == 'end') || 4208 ('rtl' == 'rtl' && 'start' == 'start')) { 4209 offset -= width; 4210 } 4211 return offset; 4212 } 4213 4214 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 4215 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 4216 const LTR_OVERRIDE = '\u202D'; 4217 const RTL_OVERRIDE = '\u202E'; 4218 const OVERRIDE_END = '\u202C'; 4219 if (direction_is_ltr) { 4220 return LTR_OVERRIDE + text + OVERRIDE_END; 4221 } 4222 return RTL_OVERRIDE + text + OVERRIDE_END; 4223 } 4224 4225 function placeAndMeasureTextInDOM(text, text_width, point) { 4226 const el = document.createElement("p"); 4227 el.innerHTML = text; 4228 el.style.font = '50px sans-serif'; 4229 el.style.direction = 'rtl'; 4230 el.style.letterSpacing = '10px'; 4231 // Put the text top left to make offsets simpler. 4232 el.style.padding = '0px'; 4233 el.style.margin = '0px'; 4234 el.style.position = 'absolute'; 4235 el.style.x = '0px'; 4236 el.style.y = '0px'; 4237 document.body.appendChild(el); 4238 text_bound = el.getBoundingClientRect(); 4239 text_x = text_bound.x; 4240 text_y = text_bound.y + text_bound.height / 2; 4241 4242 // Offset to the requested point determined by textAlign and direction. 4243 let text_align_dx = 0; 4244 if ('start' == 'center') { 4245 text_align_dx = text_width / 2; 4246 } else if ('start' == 'right' || 4247 ('rtl' == 'ltr' && 'start' == 'end') || 4248 ('rtl' == 'rtl' && 'start' == 'start')) { 4249 text_align_dx = text_width; 4250 } 4251 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 4252 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 4253 4254 document.body.removeChild(el); 4255 4256 return position.offset; 4257 } 4258 4259 ctx.font = '50px sans-serif'; 4260 ctx.direction = 'rtl'; 4261 ctx.textAlign = 'start'; 4262 ctx.letterSpacing = '10px'; 4263 4264 const kTexts = [ 4265 'UNAVAILABLE', 4266 'ππΆπ', 4267 'οΌοΌγοΌοΌ', 4268 'οΌοΌγγοΌοΌ', 4269 'γγοΌοΌγγ', 4270 '--abcd__' 4271 ] 4272 4273 for (text of kTexts) { 4274 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 4275 const tm = ctx.measureText(text); 4276 text_width = tm.width; 4277 step = 30; 4278 if ('10px' == '10px') { 4279 step = 40; 4280 } 4281 4282 offset = step; 4283 adjusted_offset = alignOffset(offset, text_width); 4284 tm_position = tm.getIndexFromOffset(adjusted_offset); 4285 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4286 assert_equals(tm_position, 4287 doc_position, 4288 "for " + text + " offset " + offset); 4289 4290 offset = text_width / 2 - 10; 4291 adjusted_offset = alignOffset(offset, text_width); 4292 tm_position = tm.getIndexFromOffset(adjusted_offset); 4293 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4294 assert_equals(tm_position, 4295 doc_position, 4296 "for " + text + " offset " + offset); 4297 4298 offset = text_width / 2 + 10; 4299 adjusted_offset = alignOffset(offset, text_width); 4300 tm_position = tm.getIndexFromOffset(adjusted_offset); 4301 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4302 assert_equals(tm_position, 4303 doc_position, 4304 "for " + text + " offset " + offset); 4305 4306 offset = text_width - step; 4307 adjusted_offset = alignOffset(offset, text_width); 4308 tm_position = tm.getIndexFromOffset(adjusted_offset); 4309 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4310 assert_equals(tm_position, 4311 doc_position, 4312 "for " + text + " offset " + offset); 4313 } 4314 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align start, 10px letter spacing and directional-override."); 4315 4316 test(t => { 4317 const canvas = document.createElement('canvas'); 4318 canvas.width = 100; 4319 canvas.height = 50; 4320 const ctx = canvas.getContext('2d'); 4321 4322 function alignOffset(offset, width) { 4323 if ('end' == 'center') { 4324 offset -= width / 2; 4325 } else if ('end' == 'right' || 4326 ('ltr' == 'ltr' && 'end' == 'end') || 4327 ('ltr' == 'rtl' && 'end' == 'start')) { 4328 offset -= width; 4329 } 4330 return offset; 4331 } 4332 4333 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 4334 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 4335 const LTR_OVERRIDE = '\u202D'; 4336 const RTL_OVERRIDE = '\u202E'; 4337 const OVERRIDE_END = '\u202C'; 4338 if (direction_is_ltr) { 4339 return LTR_OVERRIDE + text + OVERRIDE_END; 4340 } 4341 return RTL_OVERRIDE + text + OVERRIDE_END; 4342 } 4343 4344 function placeAndMeasureTextInDOM(text, text_width, point) { 4345 const el = document.createElement("p"); 4346 el.innerHTML = text; 4347 el.style.font = '50px sans-serif'; 4348 el.style.direction = 'ltr'; 4349 el.style.letterSpacing = '10px'; 4350 // Put the text top left to make offsets simpler. 4351 el.style.padding = '0px'; 4352 el.style.margin = '0px'; 4353 el.style.position = 'absolute'; 4354 el.style.x = '0px'; 4355 el.style.y = '0px'; 4356 document.body.appendChild(el); 4357 text_bound = el.getBoundingClientRect(); 4358 text_x = text_bound.x; 4359 text_y = text_bound.y + text_bound.height / 2; 4360 4361 // Offset to the requested point determined by textAlign and direction. 4362 let text_align_dx = 0; 4363 if ('end' == 'center') { 4364 text_align_dx = text_width / 2; 4365 } else if ('end' == 'right' || 4366 ('ltr' == 'ltr' && 'end' == 'end') || 4367 ('ltr' == 'rtl' && 'end' == 'start')) { 4368 text_align_dx = text_width; 4369 } 4370 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 4371 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 4372 4373 document.body.removeChild(el); 4374 4375 return position.offset; 4376 } 4377 4378 ctx.font = '50px sans-serif'; 4379 ctx.direction = 'ltr'; 4380 ctx.textAlign = 'end'; 4381 ctx.letterSpacing = '10px'; 4382 4383 const kTexts = [ 4384 'UNAVAILABLE', 4385 'ππΆπ', 4386 'οΌοΌγοΌοΌ', 4387 'οΌοΌγγοΌοΌ', 4388 'γγοΌοΌγγ', 4389 '--abcd__' 4390 ] 4391 4392 for (text of kTexts) { 4393 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 4394 const tm = ctx.measureText(text); 4395 text_width = tm.width; 4396 step = 30; 4397 if ('10px' == '10px') { 4398 step = 40; 4399 } 4400 4401 offset = step; 4402 adjusted_offset = alignOffset(offset, text_width); 4403 tm_position = tm.getIndexFromOffset(adjusted_offset); 4404 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4405 assert_equals(tm_position, 4406 doc_position, 4407 "for " + text + " offset " + offset); 4408 4409 offset = text_width / 2 - 10; 4410 adjusted_offset = alignOffset(offset, text_width); 4411 tm_position = tm.getIndexFromOffset(adjusted_offset); 4412 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4413 assert_equals(tm_position, 4414 doc_position, 4415 "for " + text + " offset " + offset); 4416 4417 offset = text_width / 2 + 10; 4418 adjusted_offset = alignOffset(offset, text_width); 4419 tm_position = tm.getIndexFromOffset(adjusted_offset); 4420 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4421 assert_equals(tm_position, 4422 doc_position, 4423 "for " + text + " offset " + offset); 4424 4425 offset = text_width - step; 4426 adjusted_offset = alignOffset(offset, text_width); 4427 tm_position = tm.getIndexFromOffset(adjusted_offset); 4428 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4429 assert_equals(tm_position, 4430 doc_position, 4431 "for " + text + " offset " + offset); 4432 } 4433 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction ltr, text align end, 10px letter spacing and directional-override."); 4434 4435 test(t => { 4436 const canvas = document.createElement('canvas'); 4437 canvas.width = 100; 4438 canvas.height = 50; 4439 const ctx = canvas.getContext('2d'); 4440 4441 function alignOffset(offset, width) { 4442 if ('end' == 'center') { 4443 offset -= width / 2; 4444 } else if ('end' == 'right' || 4445 ('rtl' == 'ltr' && 'end' == 'end') || 4446 ('rtl' == 'rtl' && 'end' == 'start')) { 4447 offset -= width; 4448 } 4449 return offset; 4450 } 4451 4452 function addDirectionalOverrideCharacters(text, direction_is_ltr) { 4453 // source: www.w3.org/International/questions/qa-bidi-unicode-controls.en 4454 const LTR_OVERRIDE = '\u202D'; 4455 const RTL_OVERRIDE = '\u202E'; 4456 const OVERRIDE_END = '\u202C'; 4457 if (direction_is_ltr) { 4458 return LTR_OVERRIDE + text + OVERRIDE_END; 4459 } 4460 return RTL_OVERRIDE + text + OVERRIDE_END; 4461 } 4462 4463 function placeAndMeasureTextInDOM(text, text_width, point) { 4464 const el = document.createElement("p"); 4465 el.innerHTML = text; 4466 el.style.font = '50px sans-serif'; 4467 el.style.direction = 'rtl'; 4468 el.style.letterSpacing = '10px'; 4469 // Put the text top left to make offsets simpler. 4470 el.style.padding = '0px'; 4471 el.style.margin = '0px'; 4472 el.style.position = 'absolute'; 4473 el.style.x = '0px'; 4474 el.style.y = '0px'; 4475 document.body.appendChild(el); 4476 text_bound = el.getBoundingClientRect(); 4477 text_x = text_bound.x; 4478 text_y = text_bound.y + text_bound.height / 2; 4479 4480 // Offset to the requested point determined by textAlign and direction. 4481 let text_align_dx = 0; 4482 if ('end' == 'center') { 4483 text_align_dx = text_width / 2; 4484 } else if ('end' == 'right' || 4485 ('rtl' == 'ltr' && 'end' == 'end') || 4486 ('rtl' == 'rtl' && 'end' == 'start')) { 4487 text_align_dx = text_width; 4488 } 4489 position = document.caretPositionFromPoint(point + text_align_dx + text_x, text_y); 4490 _assertSame(position.offsetNode.parentNode, el, "position.offsetNode.parentNode", "el"); 4491 4492 document.body.removeChild(el); 4493 4494 return position.offset; 4495 } 4496 4497 ctx.font = '50px sans-serif'; 4498 ctx.direction = 'rtl'; 4499 ctx.textAlign = 'end'; 4500 ctx.letterSpacing = '10px'; 4501 4502 const kTexts = [ 4503 'UNAVAILABLE', 4504 'ππΆπ', 4505 'οΌοΌγοΌοΌ', 4506 'οΌοΌγγοΌοΌ', 4507 'γγοΌοΌγγ', 4508 '--abcd__' 4509 ] 4510 4511 for (text of kTexts) { 4512 text = addDirectionalOverrideCharacters(text, ctx.direction == 'ltr'); 4513 const tm = ctx.measureText(text); 4514 text_width = tm.width; 4515 step = 30; 4516 if ('10px' == '10px') { 4517 step = 40; 4518 } 4519 4520 offset = step; 4521 adjusted_offset = alignOffset(offset, text_width); 4522 tm_position = tm.getIndexFromOffset(adjusted_offset); 4523 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4524 assert_equals(tm_position, 4525 doc_position, 4526 "for " + text + " offset " + offset); 4527 4528 offset = text_width / 2 - 10; 4529 adjusted_offset = alignOffset(offset, text_width); 4530 tm_position = tm.getIndexFromOffset(adjusted_offset); 4531 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4532 assert_equals(tm_position, 4533 doc_position, 4534 "for " + text + " offset " + offset); 4535 4536 offset = text_width / 2 + 10; 4537 adjusted_offset = alignOffset(offset, text_width); 4538 tm_position = tm.getIndexFromOffset(adjusted_offset); 4539 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4540 assert_equals(tm_position, 4541 doc_position, 4542 "for " + text + " offset " + offset); 4543 4544 offset = text_width - step; 4545 adjusted_offset = alignOffset(offset, text_width); 4546 tm_position = tm.getIndexFromOffset(adjusted_offset); 4547 doc_position = placeAndMeasureTextInDOM(text, text_width, adjusted_offset); 4548 assert_equals(tm_position, 4549 doc_position, 4550 "for " + text + " offset " + offset); 4551 } 4552 }, "Check that TextMetrics::getIndexFromOffset() matches its DOM equivalent, where possible, with direction rtl, text align end, 10px letter spacing and directional-override."); 4553 4554 </script>