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