1describe('Paragraph Behavior', function() { 2 let container; 3 4 const assetLoadingPromises = []; 5 let notoSerifFontBuffer = null; 6 // This font is known to support kerning 7 assetLoadingPromises.push(fetch('/assets/NotoSerif-Regular.ttf').then( 8 (response) => response.arrayBuffer()).then( 9 (buffer) => { 10 notoSerifFontBuffer = buffer; 11 })); 12 13 let notoSerifBoldItalicFontBuffer = null; 14 assetLoadingPromises.push(fetch('/assets/NotoSerif-BoldItalic.ttf').then( 15 (response) => response.arrayBuffer()).then( 16 (buffer) => { 17 notoSerifBoldItalicFontBuffer = buffer; 18 })); 19 20 let emojiFontBuffer = null; 21 assetLoadingPromises.push(fetch('/assets/NotoColorEmoji.ttf').then( 22 (response) => response.arrayBuffer()).then( 23 (buffer) => { 24 emojiFontBuffer = buffer; 25 })); 26 27 let robotoFontBuffer = null; 28 assetLoadingPromises.push(fetch('/assets/Roboto-Regular.otf').then( 29 (response) => response.arrayBuffer()).then( 30 (buffer) => { 31 robotoFontBuffer = buffer; 32 })); 33 34 let robotoVariableFontBuffer = null; 35 assetLoadingPromises.push(fetch('/assets/RobotoSlab-VariableFont_wght.ttf').then( 36 (response) => response.arrayBuffer()).then( 37 (buffer) => { 38 robotoVariableFontBuffer = buffer; 39 })); 40 41 beforeEach(async () => { 42 await EverythingLoaded; 43 await Promise.all(assetLoadingPromises); 44 container = document.createElement('div'); 45 container.innerHTML = ` 46 <canvas width=600 height=600 id=test></canvas> 47 <canvas width=600 height=600 id=report></canvas>`; 48 document.body.appendChild(container); 49 }); 50 51 afterEach(() => { 52 document.body.removeChild(container); 53 }); 54 55 gm('paragraph_basic', (canvas) => { 56 const paint = new CanvasKit.Paint(); 57 58 paint.setColor(CanvasKit.RED); 59 paint.setStyle(CanvasKit.PaintStyle.Stroke); 60 61 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 62 expect(fontMgr.countFamilies()).toEqual(1); 63 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 64 65 const wrapTo = 200; 66 67 const paraStyle = new CanvasKit.ParagraphStyle({ 68 textStyle: { 69 color: CanvasKit.BLACK, 70 fontFamilies: ['Noto Serif'], 71 fontSize: 20, 72 }, 73 textAlign: CanvasKit.TextAlign.Center, 74 maxLines: 8, 75 ellipsis: '.._.', 76 }); 77 78 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 79 builder.addText('VAVAVAVAVAVAVA\nVAVA\n'); 80 81 const blueText = new CanvasKit.TextStyle({ 82 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink 83 color: CanvasKit.Color(48, 37, 199), 84 fontFamilies: ['Noto Serif'], 85 decoration: CanvasKit.LineThroughDecoration, 86 decorationThickness: 1.5, // multiplier based on font size 87 fontSize: 24, 88 }); 89 builder.pushStyle(blueText); 90 builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`) 91 builder.pop(); 92 builder.addText(` I'm done with the blue now. `) 93 builder.addText(`Now I hope we should stop before we get 8 lines tall. `); 94 const paragraph = builder.build(); 95 96 paragraph.layout(wrapTo); 97 98 expect(paragraph.didExceedMaxLines()).toBeTruthy(); 99 expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3); 100 expect(paragraph.getHeight()).toEqual(240); 101 expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3); 102 expect(paragraph.getLongestLine()).toBeCloseTo(193.820, 3); 103 expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1444.250, 3); 104 expect(paragraph.getMaxWidth()).toEqual(200); 105 expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3); 106 expect(paragraph.getWordBoundary(8)).toEqual({ 107 start: 0, 108 end: 14, 109 }); 110 expect(paragraph.getWordBoundary(25)).toEqual({ 111 start: 25, 112 end: 26, 113 }); 114 115 116 const lineMetrics = paragraph.getLineMetrics(); 117 expect(lineMetrics.length).toEqual(8); // 8 lines worth of metrics 118 const flm = lineMetrics[0]; // First Line Metric 119 expect(flm.startIndex).toEqual(0); 120 expect(flm.endExcludingWhitespaces).toEqual(14) 121 expect(flm.endIndex).toEqual(14); // Including whitespaces but excluding newlines 122 expect(flm.endIncludingNewline).toEqual(15); 123 expect(flm.lineNumber).toEqual(0); 124 expect(flm.isHardBreak).toEqual(true); 125 expect(flm.ascent).toBeCloseTo(21.377, 3); 126 expect(flm.descent).toBeCloseTo(5.859, 3); 127 expect(flm.height).toBeCloseTo(27.000, 3); 128 expect(flm.width).toBeCloseTo(172.360, 3); 129 expect(flm.left).toBeCloseTo(13.818, 3); 130 expect(flm.baseline).toBeCloseTo(21.141, 3); 131 132 const singleLineMetrics = paragraph.getLineMetricsAt(0); 133 expect(singleLineMetrics.startIndex).toEqual(flm.startIndex); 134 expect(singleLineMetrics.lineNumber).toEqual(flm.lineNumber); 135 expect(paragraph.getLineMetricsAt(9)).toBeFalsy(); 136 expect(paragraph.getNumberOfLines()).toEqual(8); 137 expect(paragraph.getLineNumberAt(9999)).toEqual(-1); 138 expect(paragraph.getLineNumberAt(0)).toEqual(0); 139 140 const glyphInfo = paragraph.getGlyphInfoAt(13); 141 expect(glyphInfo.graphemeClusterTextRange.start).toEqual(13); 142 expect(glyphInfo.graphemeClusterTextRange.end).toEqual(14); 143 expect(glyphInfo.dir).toEqual(CanvasKit.TextDirection.LTR); 144 expect(glyphInfo.isEllipsis).toEqual(false); 145 expect(glyphInfo.graphemeLayoutBounds[0]).toBeCloseTo(172.08, 3); 146 expect(glyphInfo.graphemeLayoutBounds[1]).toBeCloseTo(-0.24, 3); 147 expect(glyphInfo.graphemeLayoutBounds[2]).toBeCloseTo(186.18, 3); 148 expect(glyphInfo.graphemeLayoutBounds[3]).toBeCloseTo(27, 3); 149 150 expect(paragraph.getGlyphInfoAt(9999)).toBeFalsy(); 151 152 // This should hit the last character on the first line. 153 const lastGlyphOnFirstLine = paragraph.getClosestGlyphInfoAtCoordinate(180, 21); 154 expect(lastGlyphOnFirstLine.graphemeClusterTextRange.start).toEqual(13); 155 expect(lastGlyphOnFirstLine.graphemeClusterTextRange.end).toEqual(14); 156 expect(lastGlyphOnFirstLine.dir).toEqual(CanvasKit.TextDirection.LTR); 157 expect(lastGlyphOnFirstLine.isEllipsis).toEqual(false); 158 expect(lastGlyphOnFirstLine.graphemeLayoutBounds[0]).toBeCloseTo(172.08, 3); 159 expect(lastGlyphOnFirstLine.graphemeLayoutBounds[1]).toBeCloseTo(-0.24, 3); 160 expect(lastGlyphOnFirstLine.graphemeLayoutBounds[2]).toBeCloseTo(186.18, 3); 161 expect(lastGlyphOnFirstLine.graphemeLayoutBounds[3]).toBeCloseTo(27, 3); 162 163 const unresolvedGlyphs = paragraph.unresolvedCodepoints(); 164 expect(unresolvedGlyphs.length).toEqual(0, unresolvedGlyphs); 165 166 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint); 167 canvas.drawParagraph(paragraph, 10, 10); 168 169 paint.delete(); 170 fontMgr.delete(); 171 paragraph.delete(); 172 builder.delete(); 173 }); 174 175 gm('paragraph_foreground_and_background_color', (canvas) => { 176 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 177 expect(fontMgr.countFamilies()).toEqual(1); 178 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 179 180 const wrapTo = 200; 181 182 const paraStyle = new CanvasKit.ParagraphStyle({ 183 textStyle: { 184 foregroundColor: CanvasKit.Color4f(1.0, 0, 0, 0.8), 185 backgroundColor: CanvasKit.Color4f(0, 0, 1.0, 0.8), 186 // color should default to black 187 fontFamilies: ['Noto Serif'], 188 fontSize: 20, 189 }, 190 191 textAlign: CanvasKit.TextAlign.Center, 192 }); 193 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 194 builder.addText( 195 'This text has a red foregroundColor and a blue backgroundColor.'); 196 const paragraph = builder.build(); 197 paragraph.layout(300); 198 canvas.drawParagraph(paragraph, 10, 10); 199 200 fontMgr.delete(); 201 paragraph.delete(); 202 builder.delete(); 203 }); 204 205 gm('paragraph_foreground_stroke_paint', (canvas) => { 206 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 207 expect(fontMgr.countFamilies()).toEqual(1); 208 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 209 210 const wrapTo = 200; 211 212 const textStyle = { 213 fontFamilies: ['Noto Serif'], 214 fontSize: 40, 215 }; 216 const paraStyle = new CanvasKit.ParagraphStyle({ 217 textStyle: textStyle, 218 textAlign: CanvasKit.TextAlign.Center, 219 }); 220 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 221 222 const fg = new CanvasKit.Paint(); 223 fg.setColor(CanvasKit.BLACK); 224 fg.setStyle(CanvasKit.PaintStyle.Stroke); 225 226 const bg = new CanvasKit.Paint(); 227 bg.setColor(CanvasKit.TRANSPARENT); 228 229 builder.pushPaintStyle(textStyle, fg, bg); 230 builder.addText( 231 'This text is stroked in black and has no fill'); 232 const paragraph = builder.build(); 233 paragraph.layout(300); 234 canvas.drawParagraph(paragraph, 10, 10); 235 // Again 5px to the right so you can tell the fill is transparent 236 canvas.drawParagraph(paragraph, 15, 10); 237 238 fg.delete(); 239 bg.delete(); 240 fontMgr.delete(); 241 paragraph.delete(); 242 builder.delete(); 243 }); 244 245 gm('paragraph_letter_word_spacing', (canvas) => { 246 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 247 expect(fontMgr.countFamilies()).toEqual(1); 248 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 249 250 const wrapTo = 200; 251 252 const paraStyle = new CanvasKit.ParagraphStyle({ 253 textStyle: { 254 // color should default to black 255 fontFamilies: ['Noto Serif'], 256 fontSize: 20, 257 letterSpacing: 5, 258 wordSpacing: 10, 259 }, 260 261 textAlign: CanvasKit.TextAlign.Center, 262 }); 263 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 264 builder.addText( 265 'This text should have a lot of space between the letters and words.'); 266 const paragraph = builder.build(); 267 paragraph.layout(300); 268 canvas.drawParagraph(paragraph, 10, 10); 269 270 fontMgr.delete(); 271 paragraph.delete(); 272 builder.delete(); 273 }); 274 275 gm('paragraph_shadows', (canvas) => { 276 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 277 expect(fontMgr.countFamilies()).toEqual(1); 278 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 279 280 const wrapTo = 200; 281 282 const paraStyle = new CanvasKit.ParagraphStyle({ 283 textStyle: { 284 color: CanvasKit.WHITE, 285 fontFamilies: ['Noto Serif'], 286 fontSize: 20, 287 shadows: [{color: CanvasKit.BLACK, blurRadius: 15}, 288 {color: CanvasKit.RED, blurRadius: 5, offset: [10, 10]}], 289 }, 290 291 textAlign: CanvasKit.TextAlign.Center, 292 }); 293 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 294 builder.addText('This text should have a shadow behind it.'); 295 const paragraph = builder.build(); 296 paragraph.layout(300); 297 canvas.drawParagraph(paragraph, 10, 10); 298 299 fontMgr.delete(); 300 paragraph.delete(); 301 builder.delete(); 302 }); 303 304 gm('paragraph_strut_style', (canvas) => { 305 const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer); 306 expect(fontMgr.countFamilies()).toEqual(1); 307 expect(fontMgr.getFamilyName(0)).toEqual('Roboto'); 308 309 // The lines in this paragraph should have the same height despite the third 310 // line having a larger font size. 311 const paraStrutStyle = new CanvasKit.ParagraphStyle({ 312 textStyle: { 313 fontFamilies: ['Roboto'], 314 color: CanvasKit.BLACK, 315 }, 316 strutStyle: { 317 strutEnabled: true, 318 fontFamilies: ['Roboto'], 319 fontSize: 28, 320 heightMultiplier: 1.5, 321 forceStrutHeight: true, 322 }, 323 }); 324 const paraStyle = new CanvasKit.ParagraphStyle({ 325 textStyle: { 326 fontFamilies: ['Roboto'], 327 color: CanvasKit.BLACK, 328 }, 329 }); 330 const roboto28Style = new CanvasKit.TextStyle({ 331 color: CanvasKit.BLACK, 332 fontFamilies: ['Roboto'], 333 fontSize: 28, 334 }); 335 const roboto32Style = new CanvasKit.TextStyle({ 336 color: CanvasKit.BLACK, 337 fontFamilies: ['Roboto'], 338 fontSize: 32, 339 }); 340 const builder = CanvasKit.ParagraphBuilder.Make(paraStrutStyle, fontMgr); 341 builder.pushStyle(roboto28Style); 342 builder.addText('This paragraph\n'); 343 builder.pushStyle(roboto32Style); 344 builder.addText('is using\n'); 345 builder.pop(); 346 builder.pushStyle(roboto28Style); 347 builder.addText('a strut style!\n'); 348 builder.pop(); 349 builder.pop(); 350 351 const builder2 = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 352 builder2.pushStyle(roboto28Style); 353 builder2.addText('This paragraph\n'); 354 builder2.pushStyle(roboto32Style); 355 builder2.addText('is not using\n'); 356 builder2.pop(); 357 builder2.pushStyle(roboto28Style); 358 builder2.addText('a strut style!\n'); 359 builder2.pop(); 360 builder2.pop(); 361 362 const paragraph = builder.build(); 363 paragraph.layout(300); 364 365 const paragraph2 = builder2.build(); 366 paragraph2.layout(300); 367 canvas.drawParagraph(paragraph, 10, 10); 368 canvas.drawParagraph(paragraph2, 220, 10); 369 370 fontMgr.delete(); 371 paragraph.delete(); 372 builder.delete(); 373 }); 374 375 gm('paragraph_font_features', (canvas) => { 376 const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer); 377 expect(fontMgr.countFamilies()).toEqual(1); 378 expect(fontMgr.getFamilyName(0)).toEqual('Roboto'); 379 380 381 const paraStyle = new CanvasKit.ParagraphStyle({ 382 textStyle: { 383 color: CanvasKit.BLACK, 384 fontFamilies: ['Roboto'], 385 fontSize: 30, 386 fontFeatures: [{name: 'smcp', value: 1}] 387 }, 388 textAlign: CanvasKit.TextAlign.Center, 389 }); 390 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 391 builder.addText('This Text Should Be In Small Caps'); 392 const paragraph = builder.build(); 393 paragraph.layout(300); 394 canvas.drawParagraph(paragraph, 10, 10); 395 396 fontMgr.delete(); 397 paragraph.delete(); 398 builder.delete(); 399 }); 400 401 gm('paragraph_font_variations', (canvas) => { 402 const fontMgr = CanvasKit.FontMgr.FromData(robotoVariableFontBuffer); 403 expect(fontMgr.countFamilies()).toEqual(1); 404 expect(fontMgr.getFamilyName(0)).toEqual('Roboto Slab'); 405 406 const paraStyle = new CanvasKit.ParagraphStyle({ 407 textStyle: { 408 color: CanvasKit.BLACK, 409 fontFamilies: ['Roboto Slab'], 410 fontSize: 30, 411 }, 412 textAlign: CanvasKit.TextAlign.Center, 413 }); 414 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 415 builder.addText('Normal\n'); 416 builder.pushStyle(new CanvasKit.TextStyle({ 417 fontFamilies: ['Roboto Slab'], 418 fontSize: 30, 419 fontVariations: [{axis: 'wght', value: 900}] 420 })); 421 builder.addText('Heavy Weight\n'); 422 builder.pushStyle(new CanvasKit.TextStyle({ 423 fontFamilies: ['Roboto Slab'], 424 fontSize: 30, 425 fontVariations: [{axis: 'wght', value: 100}] 426 })); 427 builder.addText('Light Weight\n'); 428 builder.pop(); 429 builder.pop(); 430 431 const paragraph = builder.build(); 432 paragraph.layout(300); 433 434 canvas.clear(CanvasKit.WHITE); 435 canvas.drawParagraph(paragraph, 10, 10); 436 437 fontMgr.delete(); 438 paragraph.delete(); 439 builder.delete(); 440 }); 441 442 gm('paragraph_placeholders', (canvas) => { 443 const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer); 444 expect(fontMgr.countFamilies()).toEqual(1); 445 expect(fontMgr.getFamilyName(0)).toEqual('Roboto'); 446 447 448 const paraStyle = new CanvasKit.ParagraphStyle({ 449 textStyle: { 450 color: CanvasKit.BLACK, 451 fontFamilies: ['Roboto'], 452 fontSize: 20, 453 }, 454 textAlign: CanvasKit.TextAlign.Center, 455 }); 456 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 457 builder.addText('There should be '); 458 builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.AboveBaseline, 459 CanvasKit.TextBaseline.Ideographic); 460 builder.addText('a space in this sentence.\n'); 461 462 builder.addText('There should be '); 463 builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.BelowBaseline, 464 CanvasKit.TextBaseline.Ideographic); 465 builder.addText('a dropped space in this sentence.\n'); 466 467 builder.addText('There should be '); 468 builder.addPlaceholder(10, 10, null, null, 20); 469 builder.addText('an offset space in this sentence.\n'); 470 const paragraph = builder.build(); 471 paragraph.layout(300); 472 473 let rects = paragraph.getRectsForPlaceholders(); 474 canvas.drawParagraph(paragraph, 10, 10); 475 476 for (const r of rects) { 477 const p = new CanvasKit.Paint(); 478 p.setColor(CanvasKit.Color(0, 0, 255)); 479 p.setStyle(CanvasKit.PaintStyle.Stroke); 480 // Account for the (10, 10) offset when we painted the paragraph. 481 const rect = r.rect; 482 expect(r.dir).toEqual(CanvasKit.TextDirection.LTR); 483 const placeholder = 484 CanvasKit.LTRBRect(rect[0]+10,rect[1]+10,rect[2]+10,rect[3]+10); 485 canvas.drawRect(placeholder, p); 486 p.delete(); 487 } 488 489 fontMgr.delete(); 490 paragraph.delete(); 491 builder.delete(); 492 }); 493 494 // loosely based on SkParagraph_GetRectsForRangeParagraph test in c++ code. 495 gm('paragraph_rects', (canvas) => { 496 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 497 498 const wrapTo = 550; 499 const hStyle = CanvasKit.RectHeightStyle.Max; 500 const wStyle = CanvasKit.RectWidthStyle.Tight; 501 502 const mallocedColor = CanvasKit.Malloc(Float32Array, 4); 503 mallocedColor.toTypedArray().set([0.9, 0.1, 0.1, 1.0]); 504 505 const paraStyle = new CanvasKit.ParagraphStyle({ 506 textStyle: { 507 color: mallocedColor, 508 fontFamilies: ['Noto Serif'], 509 fontSize: 50, 510 }, 511 textAlign: CanvasKit.TextAlign.Left, 512 maxLines: 10, 513 }); 514 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 515 builder.addText('12345, \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345'); 516 const paragraph = builder.build(); 517 CanvasKit.Free(mallocedColor); 518 519 paragraph.layout(wrapTo); 520 521 const ranges = [ 522 { 523 start: 0, 524 end: 0, 525 expectedNum: 0, 526 }, 527 { 528 start: 0, 529 end: 1, 530 expectedNum: 1, 531 color: CanvasKit.Color(200, 0, 200), 532 }, 533 { 534 start: 2, 535 end: 8, 536 expectedNum: 1, 537 color: CanvasKit.Color(255, 0, 0), 538 }, 539 { 540 start: 8, 541 end: 21, 542 expectedNum: 1, 543 color: CanvasKit.Color(0, 255, 0), 544 }, 545 { 546 start: 30, 547 end: 100, 548 expectedNum: 4, 549 color: CanvasKit.Color(0, 0, 255), 550 }, 551 { 552 start: 19, 553 end: 22, 554 expectedNum: 1, 555 color: CanvasKit.Color(0, 200, 200), 556 } 557 ]; 558 // Move it down a bit so we can see the rects that go above 0,0 559 canvas.translate(10, 10); 560 canvas.drawParagraph(paragraph, 0, 0); 561 562 for (const test of ranges) { 563 let rects = paragraph.getRectsForRange(test.start, test.end, hStyle, wStyle); 564 expect(Array.isArray(rects)).toEqual(true); 565 expect(rects.length).toEqual(test.expectedNum); 566 567 for (const r of rects) { 568 expect(r.dir).toEqual(CanvasKit.TextDirection.LTR); 569 const p = new CanvasKit.Paint(); 570 p.setColor(test.color); 571 p.setStyle(CanvasKit.PaintStyle.Stroke); 572 canvas.drawRect(r.rect, p); 573 p.delete(); 574 } 575 } 576 expect(CanvasKit.RectHeightStyle.Strut).toBeTruthy(); 577 578 fontMgr.delete(); 579 paragraph.delete(); 580 builder.delete(); 581 }); 582 583 gm('paragraph_emoji', (canvas) => { 584 const fontMgr = CanvasKit.FontMgr.FromData([notoSerifFontBuffer, emojiFontBuffer]); 585 expect(fontMgr.countFamilies()).toEqual(2); 586 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 587 expect(fontMgr.getFamilyName(1)).toEqual('Noto Color Emoji'); 588 589 const wrapTo = 450; 590 591 const paraStyle = new CanvasKit.ParagraphStyle({ 592 textStyle: { 593 color: CanvasKit.BLACK, 594 // Put text first, otherwise the "emoji space" is used and that looks bad. 595 fontFamilies: ['Noto Serif', 'Noto Color Emoji'], 596 fontSize: 30, 597 }, 598 textAlign: CanvasKit.TextAlign.Left, 599 maxLines: 10, 600 }); 601 602 const textStyle = new CanvasKit.TextStyle({ 603 color: CanvasKit.BLACK, 604 // The number 4 matches an emoji and looks strange w/o this additional style. 605 fontFamilies: ['Noto Serif'], 606 fontSize: 30, 607 }); 608 609 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 610 builder.pushStyle(textStyle); 611 builder.addText('4 flags on following line:\n'); 612 builder.pop(); 613 builder.addText(`️ \n`); 614 builder.addText('Rainbow Italy Liberia USA\n\n'); 615 builder.addText('Emoji below should wrap:\n'); 616 builder.addText(``); 617 const paragraph = builder.build(); 618 619 paragraph.layout(wrapTo); 620 canvas.drawParagraph(paragraph, 10, 10); 621 622 const paint = new CanvasKit.Paint(); 623 paint.setColor(CanvasKit.RED); 624 paint.setStyle(CanvasKit.PaintStyle.Stroke); 625 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint); 626 627 fontMgr.delete(); 628 paint.delete(); 629 builder.delete(); 630 paragraph.delete(); 631 }); 632 633 gm('paragraph_hits', (canvas) => { 634 const fontMgr = CanvasKit.FontMgr.FromData([notoSerifFontBuffer]); 635 636 const wrapTo = 300; 637 638 const paraStyle = new CanvasKit.ParagraphStyle({ 639 textStyle: { 640 color: CanvasKit.BLACK, 641 fontFamilies: ['Noto Serif'], 642 fontSize: 50, 643 }, 644 textAlign: CanvasKit.TextAlign.Left, 645 maxLines: 10, 646 }); 647 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 648 builder.addText('UNCOPYRIGHTABLE'); 649 const paragraph = builder.build(); 650 651 paragraph.layout(wrapTo); 652 canvas.translate(10, 10); 653 canvas.drawParagraph(paragraph, 0, 0); 654 655 const paint = new CanvasKit.Paint(); 656 657 paint.setColor(CanvasKit.Color(255, 0, 0)); 658 paint.setStyle(CanvasKit.PaintStyle.Fill); 659 canvas.drawCircle(20, 30, 3, paint); 660 661 paint.setColor(CanvasKit.Color(0, 0, 255)); 662 canvas.drawCircle(80, 90, 3, paint); 663 664 paint.setColor(CanvasKit.Color(0, 255, 0)); 665 canvas.drawCircle(280, 2, 3, paint); 666 667 let posU = paragraph.getGlyphPositionAtCoordinate(20, 30); 668 expect(posU).toEqual({ 669 pos: 1, 670 affinity: CanvasKit.Affinity.Upstream 671 }); 672 let posA = paragraph.getGlyphPositionAtCoordinate(80, 90); 673 expect(posA).toEqual({ 674 pos: 11, 675 affinity: CanvasKit.Affinity.Downstream 676 }); 677 let posG = paragraph.getGlyphPositionAtCoordinate(280, 2); 678 expect(posG).toEqual({ 679 pos: 9, 680 affinity: CanvasKit.Affinity.Upstream 681 }); 682 683 builder.delete(); 684 paragraph.delete(); 685 paint.delete(); 686 fontMgr.delete(); 687 }); 688 689 gm('paragraph_styles', (canvas) => { 690 const paint = new CanvasKit.Paint(); 691 692 paint.setColor(CanvasKit.RED); 693 paint.setStyle(CanvasKit.PaintStyle.Stroke); 694 695 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer); 696 697 const wrapTo = 250; 698 699 const paraStyle = new CanvasKit.ParagraphStyle({ 700 textStyle: { 701 fontFamilies: ['Noto Serif'], 702 fontSize: 20, 703 fontStyle: { 704 weight: CanvasKit.FontWeight.Light, 705 } 706 }, 707 textDirection: CanvasKit.TextDirection.RTL, 708 disableHinting: true, 709 }); 710 711 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 712 builder.addText('Default text\n'); 713 714 const boldItalic = new CanvasKit.TextStyle({ 715 color: CanvasKit.RED, 716 fontFamilies: ['Noto Serif'], 717 fontSize: 20, 718 fontStyle: { 719 weight: CanvasKit.FontWeight.Bold, 720 width: CanvasKit.FontWidth.Expanded, 721 slant: CanvasKit.FontSlant.Italic, 722 } 723 }); 724 builder.pushStyle(boldItalic); 725 builder.addText(`Bold, Expanded, Italic\n`); 726 builder.pop(); 727 builder.addText(`back to normal`); 728 const paragraph = builder.build(); 729 730 paragraph.layout(wrapTo); 731 732 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint); 733 canvas.drawParagraph(paragraph, 10, 10); 734 735 paint.delete(); 736 paragraph.delete(); 737 builder.delete(); 738 fontMgr.delete(); 739 }); 740 741 it('paragraph_rounding_hack', () => { 742 const paraStyleDefault = new CanvasKit.ParagraphStyle({ 743 textStyle: { 744 fontFamilies: ['Noto Serif'], 745 fontSize: 20, 746 fontStyle: { 747 weight: CanvasKit.FontWeight.Light, 748 } 749 }, 750 textDirection: CanvasKit.TextDirection.RTL, 751 disableHinting: true, 752 }); 753 expect(paraStyleDefault.applyRoundingHack).toEqual(true); 754 755 const paraStyleOverride = new CanvasKit.ParagraphStyle({ 756 textStyle: { 757 fontFamilies: ['Noto Serif'], 758 fontSize: 20, 759 fontStyle: { 760 weight: CanvasKit.FontWeight.Light, 761 } 762 }, 763 textDirection: CanvasKit.TextDirection.RTL, 764 disableHinting: true, 765 applyRoundingHack: false, 766 }); 767 expect(paraStyleOverride.applyRoundingHack).toEqual(false); 768 }); 769 770 gm('paragraph_font_provider', (canvas) => { 771 const paint = new CanvasKit.Paint(); 772 773 paint.setColor(CanvasKit.RED); 774 paint.setStyle(CanvasKit.PaintStyle.Stroke); 775 776 // Register Noto Serif as 'sans-serif'. 777 const fontSrc = CanvasKit.TypefaceFontProvider.Make(); 778 fontSrc.registerFont(notoSerifFontBuffer, 'sans-serif'); 779 fontSrc.registerFont(notoSerifBoldItalicFontBuffer, 'sans-serif'); 780 781 const wrapTo = 250; 782 783 const paraStyle = new CanvasKit.ParagraphStyle({ 784 textStyle: { 785 fontFamilies: ['sans-serif'], 786 fontSize: 20, 787 fontStyle: { 788 weight: CanvasKit.FontWeight.Light, 789 } 790 }, 791 textDirection: CanvasKit.TextDirection.RTL, 792 disableHinting: true, 793 }); 794 795 const builder = CanvasKit.ParagraphBuilder.MakeFromFontProvider(paraStyle, fontSrc); 796 builder.addText('Default text\n'); 797 798 const boldItalic = new CanvasKit.TextStyle({ 799 color: CanvasKit.RED, 800 fontFamilies: ['sans-serif'], 801 fontSize: 20, 802 fontStyle: { 803 weight: CanvasKit.FontWeight.Bold, 804 width: CanvasKit.FontWidth.Expanded, 805 slant: CanvasKit.FontSlant.Italic, 806 } 807 }); 808 builder.pushStyle(boldItalic); 809 builder.addText(`Bold, Expanded, Italic\n`); 810 builder.pop(); 811 builder.addText(`back to normal`); 812 const paragraph = builder.build(); 813 814 paragraph.layout(wrapTo); 815 816 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint); 817 canvas.drawParagraph(paragraph, 10, 10); 818 819 paint.delete(); 820 paragraph.delete(); 821 builder.delete(); 822 fontSrc.delete(); 823 }); 824 825 gm('paragraph_font_collection', (canvas) => { 826 const paint = new CanvasKit.Paint(); 827 828 paint.setColor(CanvasKit.RED); 829 paint.setStyle(CanvasKit.PaintStyle.Stroke); 830 831 // Register Noto Serif as 'sans-serif'. 832 const fontSrc = CanvasKit.TypefaceFontProvider.Make(); 833 fontSrc.registerFont(notoSerifFontBuffer, 'sans-serif'); 834 const fontCollection = CanvasKit.FontCollection.Make(); 835 fontCollection.setDefaultFontManager(fontSrc); 836 837 const wrapTo = 250; 838 839 const paraStyle = new CanvasKit.ParagraphStyle({ 840 textStyle: { 841 fontFamilies: ['sans-serif'], 842 fontSize: 20, 843 }, 844 disableHinting: true, 845 }); 846 847 const builder = CanvasKit.ParagraphBuilder.MakeFromFontCollection( 848 paraStyle, fontCollection); 849 builder.addText('ABC DEF GHI'); 850 851 const paragraph = builder.build(); 852 paragraph.layout(wrapTo); 853 canvas.drawParagraph(paragraph, 10, 10); 854 855 paint.delete(); 856 paragraph.delete(); 857 builder.delete(); 858 fontSrc.delete(); 859 }); 860 861 gm('paragraph_text_styles', (canvas) => { 862 const paint = new CanvasKit.Paint(); 863 864 paint.setColor(CanvasKit.GREEN); 865 paint.setStyle(CanvasKit.PaintStyle.Stroke); 866 867 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 868 expect(fontMgr.countFamilies()).toEqual(1); 869 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 870 871 const wrapTo = 200; 872 873 const paraStyle = new CanvasKit.ParagraphStyle({ 874 textStyle: { 875 color: CanvasKit.BLACK, 876 fontFamilies: ['Noto Serif'], 877 fontSize: 20, 878 decoration: CanvasKit.UnderlineDecoration, 879 decorationThickness: 1.5, // multiplier based on font size 880 decorationStyle: CanvasKit.DecorationStyle.Wavy, 881 }, 882 }); 883 884 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 885 builder.addText('VAVAVAVAVAVAVA\nVAVA\n'); 886 887 const blueText = new CanvasKit.TextStyle({ 888 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink 889 color: CanvasKit.Color(48, 37, 199), 890 fontFamilies: ['Noto Serif'], 891 textBaseline: CanvasKit.TextBaseline.Ideographic, 892 decoration: CanvasKit.LineThroughDecoration, 893 decorationThickness: 1.5, // multiplier based on font size 894 }); 895 builder.pushStyle(blueText); 896 builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`); 897 builder.pop(); 898 builder.addText(` I'm done with the blue now. `); 899 builder.addText(`Now I hope we should stop before we get 8 lines tall. `); 900 const paragraph = builder.build(); 901 902 paragraph.layout(wrapTo); 903 904 expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3); 905 expect(paragraph.getHeight()).toEqual(227); 906 expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3); 907 expect(paragraph.getLongestLine()).toBeCloseTo(195.664, 3); 908 expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1167.140, 3); 909 expect(paragraph.getMaxWidth()).toEqual(200); 910 expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3); 911 // Check "VAVAVAVAVAVAVA" 912 expect(paragraph.getWordBoundary(8)).toEqual({ 913 start: 0, 914 end: 14, 915 }); 916 // Check "I" 917 expect(paragraph.getWordBoundary(25)).toEqual({ 918 start: 25, 919 end: 26, 920 }); 921 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint); 922 canvas.drawParagraph(paragraph, 10, 10); 923 924 paint.delete(); 925 fontMgr.delete(); 926 paragraph.delete(); 927 builder.delete(); 928 }); 929 930 gm('paragraph_text_styles_mixed_leading_distribution', (canvas) => { 931 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 932 expect(fontMgr.countFamilies()).toEqual(1); 933 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 934 935 const wrapTo = 200; 936 937 const paraStyle = new CanvasKit.ParagraphStyle({ 938 textStyle: { 939 color: CanvasKit.BLACK, 940 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink 941 fontFamilies: ['Noto Serif'], 942 fontSize: 10, 943 heightMultiplier: 10, 944 }, 945 }); 946 947 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 948 builder.addText('Not half leading'); 949 950 const halfLeadingText = new CanvasKit.TextStyle({ 951 color: CanvasKit.Color(48, 37, 199), 952 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink 953 fontFamilies: ['Noto Serif'], 954 fontSize: 10, 955 heightMultiplier: 10, 956 halfLeading: true, 957 }); 958 builder.pushStyle(halfLeadingText); 959 builder.addText('Half Leading Text'); 960 const paragraph = builder.build(); 961 962 paragraph.layout(wrapTo); 963 canvas.drawParagraph(paragraph, 0, 0); 964 965 fontMgr.delete(); 966 paragraph.delete(); 967 builder.delete(); 968 }); 969 970 gm('paragraph_mixed_text_height_behavior', (canvas) => { 971 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer); 972 expect(fontMgr.countFamilies()).toEqual(1); 973 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif'); 974 const paint = new CanvasKit.Paint(); 975 paint.setColor(CanvasKit.RED); 976 paint.setStyle(CanvasKit.PaintStyle.Stroke); 977 978 const wrapTo = 220; 979 const behaviors = ["All", "DisableFirstAscent", "DisableLastDescent", "DisableAll"]; 980 981 for (let i = 0; i < behaviors.length; i++) { 982 const style = new CanvasKit.ParagraphStyle({ 983 textStyle: { 984 color: CanvasKit.BLACK, 985 fontFamilies: ['Noto Serif'], 986 fontSize: 20, 987 heightMultiplier: 3, // make the difference more obvious 988 }, 989 textHeightBehavior: CanvasKit.TextHeightBehavior[behaviors[i]], 990 }); 991 const builder = CanvasKit.ParagraphBuilder.Make(style, fontMgr); 992 builder.addText('Text height behavior\nof '+behaviors[i]); 993 const paragraph = builder.build(); 994 paragraph.layout(wrapTo); 995 canvas.drawParagraph(paragraph, 0, 150 * i); 996 canvas.drawRect(CanvasKit.LTRBRect(0, 150 * i, wrapTo, 150 * i + 120), paint); 997 paragraph.delete(); 998 builder.delete(); 999 } 1000 paint.delete(); 1001 fontMgr.delete(); 1002 }); 1003 1004 it('should not crash if we omit font family on pushed textStyle', () => { 1005 const surface = CanvasKit.MakeCanvasSurface('test'); 1006 expect(surface).toBeTruthy('Could not make surface'); 1007 1008 const canvas = surface.getCanvas(); 1009 const paint = new CanvasKit.Paint(); 1010 1011 paint.setColor(CanvasKit.RED); 1012 paint.setStyle(CanvasKit.PaintStyle.Stroke); 1013 1014 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer); 1015 1016 const wrapTo = 250; 1017 1018 const paraStyle = new CanvasKit.ParagraphStyle({ 1019 textStyle: { 1020 fontFamilies: ['Noto Serif'], 1021 fontSize: 20, 1022 }, 1023 textDirection: CanvasKit.TextDirection.RTL, 1024 disableHinting: true, 1025 }); 1026 1027 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 1028 builder.addText('Default text\n'); 1029 1030 const boldItalic = new CanvasKit.TextStyle({ 1031 fontStyle: { 1032 weight: CanvasKit.FontWeight.Bold, 1033 slant: CanvasKit.FontSlant.Italic, 1034 } 1035 }); 1036 builder.pushStyle(boldItalic); 1037 builder.addText(`Bold, Italic\n`); // doesn't show up, but we don't crash 1038 builder.pop(); 1039 builder.addText(`back to normal`); 1040 const paragraph = builder.build(); 1041 1042 paragraph.layout(wrapTo); 1043 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint); 1044 canvas.drawParagraph(paragraph, 10, 10); 1045 1046 surface.flush(); 1047 1048 paragraph.delete(); 1049 builder.delete(); 1050 paint.delete(); 1051 fontMgr.delete(); 1052 }); 1053 1054 it('should not crash if we omit font family on paragraph style', () => { 1055 const surface = CanvasKit.MakeCanvasSurface('test'); 1056 expect(surface).toBeTruthy('Could not make surface'); 1057 1058 const canvas = surface.getCanvas(); 1059 const paint = new CanvasKit.Paint(); 1060 1061 paint.setColor(CanvasKit.RED); 1062 paint.setStyle(CanvasKit.PaintStyle.Stroke); 1063 1064 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer); 1065 1066 const wrapTo = 250; 1067 1068 const paraStyle = new CanvasKit.ParagraphStyle({ 1069 textStyle: { 1070 fontSize: 20, 1071 }, 1072 textDirection: CanvasKit.TextDirection.RTL, 1073 disableHinting: true, 1074 }); 1075 1076 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 1077 builder.addText('Default text\n'); 1078 1079 const boldItalic = new CanvasKit.TextStyle({ 1080 fontStyle: { 1081 weight: CanvasKit.FontWeight.Bold, 1082 slant: CanvasKit.FontSlant.Italic, 1083 } 1084 }); 1085 builder.pushStyle(boldItalic); 1086 builder.addText(`Bold, Italic\n`); 1087 builder.pop(); 1088 builder.addText(`back to normal`); 1089 const paragraph = builder.build(); 1090 1091 paragraph.layout(wrapTo); 1092 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint); 1093 canvas.drawParagraph(paragraph, 10, 10); 1094 1095 surface.flush(); 1096 1097 paragraph.delete(); 1098 paint.delete(); 1099 fontMgr.delete(); 1100 builder.delete(); 1101 }); 1102 1103 gm('paragraph_builder_with_reset', (canvas) => { 1104 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer); 1105 1106 const wrapTo = 250; 1107 1108 const paraStyle = new CanvasKit.ParagraphStyle({ 1109 textStyle: { 1110 fontSize: 20, 1111 }, 1112 }); 1113 1114 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 1115 builder.addText('Default text\n'); 1116 1117 const boldItalic = new CanvasKit.TextStyle({ 1118 fontStyle: { 1119 weight: CanvasKit.FontWeight.Bold, 1120 slant: CanvasKit.FontSlant.Italic, 1121 } 1122 }); 1123 builder.pushStyle(boldItalic); 1124 builder.addText(`Bold, Italic\n`); 1125 builder.pop(); 1126 const paragraph = builder.build(); 1127 paragraph.layout(wrapTo); 1128 1129 builder.reset(); 1130 builder.addText('This builder has been reused\n'); 1131 1132 builder.pushStyle(boldItalic); 1133 builder.addText(`2 Bold, Italic\n`); 1134 builder.pop(); 1135 builder.addText(`2 back to normal`); 1136 const paragraph2 = builder.build(); 1137 paragraph2.layout(wrapTo); 1138 1139 canvas.drawParagraph(paragraph, 10, 10); 1140 canvas.drawParagraph(paragraph2, 10, 100); 1141 1142 paragraph.delete(); 1143 paragraph2.delete(); 1144 fontMgr.delete(); 1145 builder.delete(); 1146 }); 1147 1148 // This helped find and resolve skbug.com/13247 1149 gm('paragraph_saved_to_skpicture', (canvas) => { 1150 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer); 1151 1152 const wrapTo = 250; 1153 1154 const paraStyle = new CanvasKit.ParagraphStyle({ 1155 textStyle: { 1156 fontSize: 20, 1157 }, 1158 }); 1159 1160 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 1161 builder.addText('This was saved to an SkPicture\n'); 1162 1163 const boldItalic = new CanvasKit.TextStyle({ 1164 fontStyle: { 1165 weight: CanvasKit.FontWeight.Bold, 1166 slant: CanvasKit.FontSlant.Italic, 1167 } 1168 }); 1169 builder.pushStyle(boldItalic); 1170 builder.addText(`Bold, Italic\n`); 1171 builder.pop(); 1172 const paragraph = builder.build(); 1173 paragraph.layout(wrapTo); 1174 1175 const recorder = new CanvasKit.PictureRecorder(); 1176 const skpCanvas = recorder.beginRecording(CanvasKit.LTRBRect(0, 0, 200, 200)); 1177 skpCanvas.drawParagraph(paragraph, 10, 10); 1178 const picture = recorder.finishRecordingAsPicture(); 1179 1180 canvas.drawPicture(CanvasKit.MakePicture(picture.serialize())); 1181 1182 picture.delete(); 1183 recorder.delete(); 1184 paragraph.delete(); 1185 fontMgr.delete(); 1186 builder.delete(); 1187 }); 1188 1189 it('should replace tab characters', () => { 1190 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer); 1191 const wrapTo = 250; 1192 1193 const paraStyle = new CanvasKit.ParagraphStyle({ 1194 textStyle: { 1195 color: CanvasKit.BLACK, 1196 fontFamilies: ['Noto Serif'], 1197 fontSize: 20, 1198 }, 1199 textAlign: CanvasKit.TextAlign.Left, 1200 replaceTabCharacters: true, 1201 }); 1202 1203 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 1204 builder.addText('1\t2'); 1205 1206 const paragraph = builder.build(); 1207 paragraph.layout(wrapTo); 1208 1209 const lines = paragraph.getShapedLines(); 1210 1211 expect(lines.length).toEqual(1); 1212 expect(lines[0].runs.length).toEqual(1); 1213 expect(lines[0].runs[0].glyphs.length).toEqual(3); 1214 1215 // The tab should not be a missing glyph. 1216 expect(lines[0].runs[0].glyphs[1]).not.toEqual(0); 1217 1218 paragraph.delete(); 1219 fontMgr.delete(); 1220 builder.delete(); 1221 }); 1222 1223 gm('paragraph_fontSize_and_heightMultiplier_0', (canvas) => { 1224 const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer); 1225 const wrapTo = 250; 1226 const paraStyle = new CanvasKit.ParagraphStyle({ 1227 textStyle: { 1228 fontSize: 0, 1229 heightMultiplier: 0, 1230 }, 1231 }); 1232 1233 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr); 1234 builder.addText('This should not be visible'); 1235 const paragraph = builder.build(); 1236 paragraph.layout(wrapTo); 1237 canvas.drawParagraph(paragraph, 10, 10); 1238 1239 let rects = paragraph.getRectsForRange(0, 1, CanvasKit.RectHeightStyle.Tight, CanvasKit.RectWidthStyle.Tight); 1240 const paint = new CanvasKit.Paint(); 1241 paint.setColor(CanvasKit.Color(255, 0, 0)); 1242 canvas.drawRect(rects[0].rect, paint); 1243 expect(rects[0].dir).toEqual(CanvasKit.TextDirection.LTR); 1244 paint.delete(); 1245 1246 paragraph.delete(); 1247 fontMgr.delete(); 1248 builder.delete(); 1249 }); 1250 1251}); 1252