1 describe('Paragraph Behavior', function() {
4 let notoSerifFontBuffer = null;
5 // This font is known to support kerning
6 const notoSerifFontLoaded = fetch('/assets/NotoSerif-Regular.ttf').then(
7 (response) => response.arrayBuffer()).then(
9 notoSerifFontBuffer = buffer;
12 let notoSerifBoldItalicFontBuffer = null;
13 const notoSerifBoldItalicFontLoaded = fetch('/assets/NotoSerif-BoldItalic.ttf').then(
14 (response) => response.arrayBuffer()).then(
16 notoSerifBoldItalicFontBuffer = buffer;
19 let emojiFontBuffer = null;
20 const emojiFontLoaded = fetch('/assets/NotoColorEmoji.ttf').then(
21 (response) => response.arrayBuffer()).then(
23 emojiFontBuffer = buffer;
26 let robotoFontBuffer = null;
27 const robotoFontLoaded = fetch('/assets/Roboto-Regular.otf').then(
28 (response) => response.arrayBuffer()).then(
30 robotoFontBuffer = buffer;
33 beforeEach(async () => {
34 await EverythingLoaded;
35 await notoSerifFontLoaded;
36 await notoSerifBoldItalicFontLoaded;
37 await emojiFontLoaded;
38 await robotoFontLoaded;
39 container = document.createElement('div');
40 container.innerHTML = `
41 <canvas width=600 height=600 id=test></canvas>
42 <canvas width=600 height=600 id=report></canvas>`;
43 document.body.appendChild(container);
47 document.body.removeChild(container);
50 gm('paragraph_basic', (canvas) => {
51 const paint = new CanvasKit.Paint();
53 paint.setColor(CanvasKit.RED);
54 paint.setStyle(CanvasKit.PaintStyle.Stroke);
56 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
57 expect(fontMgr.countFamilies()).toEqual(1);
58 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
62 const paraStyle = new CanvasKit.ParagraphStyle({
64 color: CanvasKit.BLACK,
65 fontFamilies: ['Noto Serif'],
68 textAlign: CanvasKit.TextAlign.Center,
73 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
74 builder.addText('VAVAVAVAVAVAVA\nVAVA\n');
76 const blueText = new CanvasKit.TextStyle({
77 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
78 color: CanvasKit.Color(48, 37, 199),
79 fontFamilies: ['Noto Serif'],
80 decoration: CanvasKit.LineThroughDecoration,
81 decorationThickness: 1.5, // multiplier based on font size
84 builder.pushStyle(blueText);
85 builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`)
87 builder.addText(` I'm done with the blue now. `)
88 builder.addText(`Now I hope we should stop before we get 8 lines tall. `);
89 const paragraph = builder.build();
91 paragraph.layout(wrapTo);
93 expect(paragraph.didExceedMaxLines()).toBeTruthy();
94 expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3);
95 expect(paragraph.getHeight()).toEqual(240);
96 expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3);
97 expect(paragraph.getLongestLine()).toBeCloseTo(193.820, 3);
98 expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1444.250, 3);
99 expect(paragraph.getMaxWidth()).toEqual(200);
100 expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3);
101 expect(paragraph.getWordBoundary(8)).toEqual({
105 expect(paragraph.getWordBoundary(25)).toEqual({
111 const lineMetrics = paragraph.getLineMetrics();
112 expect(lineMetrics.length).toEqual(8); // 8 lines worth of metrics
113 const flm = lineMetrics[0]; // First Line Metric
114 expect(flm.startIndex).toEqual(0);
115 expect(flm.endExcludingWhitespaces).toEqual(14)
116 expect(flm.endIndex).toEqual(14); // Including whitespaces but excluding newlines
117 expect(flm.endIncludingNewline).toEqual(15);
118 expect(flm.lineNumber).toEqual(0);
119 expect(flm.isHardBreak).toEqual(true);
120 expect(flm.ascent).toBeCloseTo(21.377, 3);
121 expect(flm.descent).toBeCloseTo(5.859, 3);
122 expect(flm.height).toBeCloseTo(27.000, 3);
123 expect(flm.width).toBeCloseTo(172.360, 3);
124 expect(flm.left).toBeCloseTo(13.818, 3);
125 expect(flm.baseline).toBeCloseTo(21.141, 3);
127 canvas.clear(CanvasKit.WHITE);
128 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
129 canvas.drawParagraph(paragraph, 10, 10);
137 gm('paragraph_foreground_and_background_color', (canvas) => {
138 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
139 expect(fontMgr.countFamilies()).toEqual(1);
140 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
144 const paraStyle = new CanvasKit.ParagraphStyle({
146 foregroundColor: CanvasKit.Color4f(1.0, 0, 0, 0.8),
147 backgroundColor: CanvasKit.Color4f(0, 0, 1.0, 0.8),
148 // color should default to black
149 fontFamilies: ['Noto Serif'],
153 textAlign: CanvasKit.TextAlign.Center,
155 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
157 'This text has a red foregroundColor and a blue backgroundColor.');
158 const paragraph = builder.build();
159 paragraph.layout(300);
161 canvas.clear(CanvasKit.WHITE);
162 canvas.drawParagraph(paragraph, 10, 10);
169 gm('paragraph_foreground_stroke_paint', (canvas) => {
170 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
171 expect(fontMgr.countFamilies()).toEqual(1);
172 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
177 fontFamilies: ['Noto Serif'],
180 const paraStyle = new CanvasKit.ParagraphStyle({
181 textStyle: textStyle,
182 textAlign: CanvasKit.TextAlign.Center,
184 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
186 const fg = new CanvasKit.Paint();
187 fg.setColor(CanvasKit.BLACK);
188 fg.setStyle(CanvasKit.PaintStyle.Stroke);
190 const bg = new CanvasKit.Paint();
191 bg.setColor(CanvasKit.TRANSPARENT);
193 builder.pushPaintStyle(textStyle, fg, bg);
195 'This text is stroked in black and has no fill');
196 const paragraph = builder.build();
197 paragraph.layout(300);
199 canvas.clear(CanvasKit.WHITE);
200 canvas.drawParagraph(paragraph, 10, 10);
201 // Again 5px to the right so you can tell the fill is transparent
202 canvas.drawParagraph(paragraph, 15, 10);
211 gm('paragraph_letter_word_spacing', (canvas) => {
212 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
213 expect(fontMgr.countFamilies()).toEqual(1);
214 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
218 const paraStyle = new CanvasKit.ParagraphStyle({
220 // color should default to black
221 fontFamilies: ['Noto Serif'],
227 textAlign: CanvasKit.TextAlign.Center,
229 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
231 'This text should have a lot of space between the letters and words.');
232 const paragraph = builder.build();
233 paragraph.layout(300);
235 canvas.clear(CanvasKit.WHITE);
236 canvas.drawParagraph(paragraph, 10, 10);
243 gm('paragraph_shadows', (canvas) => {
244 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
245 expect(fontMgr.countFamilies()).toEqual(1);
246 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
250 const paraStyle = new CanvasKit.ParagraphStyle({
252 color: CanvasKit.WHITE,
253 fontFamilies: ['Noto Serif'],
255 shadows: [{color: CanvasKit.BLACK, blurRadius: 15},
256 {color: CanvasKit.RED, blurRadius: 5, offset: [10, 10]}],
259 textAlign: CanvasKit.TextAlign.Center,
261 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
262 builder.addText('This text should have a shadow behind it.');
263 const paragraph = builder.build();
264 paragraph.layout(300);
266 canvas.clear(CanvasKit.WHITE);
267 canvas.drawParagraph(paragraph, 10, 10);
274 gm('paragraph_strut_style', (canvas) => {
275 const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer);
276 expect(fontMgr.countFamilies()).toEqual(1);
277 expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
279 // The lines in this paragraph should have the same height despite the third
280 // line having a larger font size.
281 const paraStrutStyle = new CanvasKit.ParagraphStyle({
283 fontFamilies: ['Roboto'],
284 color: CanvasKit.BLACK,
288 fontFamilies: ['Roboto'],
290 heightMultiplier: 1.5,
291 forceStrutHeight: true,
294 const paraStyle = new CanvasKit.ParagraphStyle({
296 fontFamilies: ['Roboto'],
297 color: CanvasKit.BLACK,
300 const roboto28Style = new CanvasKit.TextStyle({
301 color: CanvasKit.BLACK,
302 fontFamilies: ['Roboto'],
305 const roboto32Style = new CanvasKit.TextStyle({
306 color: CanvasKit.BLACK,
307 fontFamilies: ['Roboto'],
310 const builder = CanvasKit.ParagraphBuilder.Make(paraStrutStyle, fontMgr);
311 builder.pushStyle(roboto28Style);
312 builder.addText('This paragraph\n');
313 builder.pushStyle(roboto32Style);
314 builder.addText('is using\n');
316 builder.pushStyle(roboto28Style);
317 builder.addText('a strut style!\n');
321 const builder2 = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
322 builder2.pushStyle(roboto28Style);
323 builder2.addText('This paragraph\n');
324 builder2.pushStyle(roboto32Style);
325 builder2.addText('is not using\n');
327 builder2.pushStyle(roboto28Style);
328 builder2.addText('a strut style!\n');
332 const paragraph = builder.build();
333 paragraph.layout(300);
335 const paragraph2 = builder2.build();
336 paragraph2.layout(300);
338 canvas.clear(CanvasKit.WHITE);
339 canvas.drawParagraph(paragraph, 10, 10);
340 canvas.drawParagraph(paragraph2, 220, 10);
347 gm('paragraph_font_features', (canvas) => {
348 const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer);
349 expect(fontMgr.countFamilies()).toEqual(1);
350 expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
353 const paraStyle = new CanvasKit.ParagraphStyle({
355 color: CanvasKit.BLACK,
356 fontFamilies: ['Roboto'],
358 fontFeatures: [{name: 'smcp', value: 1}]
360 textAlign: CanvasKit.TextAlign.Center,
362 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
363 builder.addText('This Text Should Be In Small Caps');
364 const paragraph = builder.build();
365 paragraph.layout(300);
367 canvas.clear(CanvasKit.WHITE);
368 canvas.drawParagraph(paragraph, 10, 10);
375 gm('paragraph_placeholders', (canvas) => {
376 const fontMgr = CanvasKit.FontMgr.FromData(robotoFontBuffer);
377 expect(fontMgr.countFamilies()).toEqual(1);
378 expect(fontMgr.getFamilyName(0)).toEqual('Roboto');
381 const paraStyle = new CanvasKit.ParagraphStyle({
383 color: CanvasKit.BLACK,
384 fontFamilies: ['Roboto'],
387 textAlign: CanvasKit.TextAlign.Center,
389 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
390 builder.addText('There should be ');
391 builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.AboveBaseline,
392 CanvasKit.TextBaseline.Ideographic);
393 builder.addText('a space in this sentence.\n');
395 builder.addText('There should be ');
396 builder.addPlaceholder(10, 10, CanvasKit.PlaceholderAlignment.BelowBaseline,
397 CanvasKit.TextBaseline.Ideographic);
398 builder.addText('a dropped space in this sentence.\n');
400 builder.addText('There should be ');
401 builder.addPlaceholder(10, 10, null, null, 20);
402 builder.addText('an offset space in this sentence.\n');
403 const paragraph = builder.build();
404 paragraph.layout(300);
406 let rects = paragraph.getRectsForPlaceholders();
408 canvas.clear(CanvasKit.WHITE);
409 canvas.drawParagraph(paragraph, 10, 10);
411 for (const rect of rects) {
412 const p = new CanvasKit.Paint();
413 p.setColor(CanvasKit.Color(0, 0, 255));
414 p.setStyle(CanvasKit.PaintStyle.Stroke);
415 // Account for the (10, 10) offset when we painted the paragraph.
417 CanvasKit.LTRBRect(rect[0]+10,rect[1]+10,rect[2]+10,rect[3]+10);
418 canvas.drawRect(placeholder, p);
427 // loosely based on SkParagraph_GetRectsForRangeParagraph test in c++ code.
428 gm('paragraph_rects', (canvas) => {
429 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
432 const hStyle = CanvasKit.RectHeightStyle.Max;
433 const wStyle = CanvasKit.RectWidthStyle.Tight;
435 const mallocedColor = CanvasKit.Malloc(Float32Array, 4);
436 mallocedColor.toTypedArray().set([0.9, 0.1, 0.1, 1.0]);
438 const paraStyle = new CanvasKit.ParagraphStyle({
440 color: mallocedColor,
441 fontFamilies: ['Noto Serif'],
444 textAlign: CanvasKit.TextAlign.Left,
447 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
448 builder.addText('12345, \"67890\" 12345 67890 12345 67890 12345 67890 12345 67890 12345 67890 12345');
449 const paragraph = builder.build();
450 CanvasKit.Free(mallocedColor);
452 paragraph.layout(wrapTo);
464 color: CanvasKit.Color(200, 0, 200),
470 color: CanvasKit.Color(255, 0, 0),
476 color: CanvasKit.Color(0, 255, 0),
482 color: CanvasKit.Color(0, 0, 255),
488 color: CanvasKit.Color(0, 200, 200),
491 canvas.clear(CanvasKit.WHITE);
492 // Move it down a bit so we can see the rects that go above 0,0
493 canvas.translate(10, 10);
494 canvas.drawParagraph(paragraph, 0, 0);
496 for (const test of ranges) {
497 let rects = paragraph.getRectsForRange(test.start, test.end, hStyle, wStyle);
498 expect(Array.isArray(rects)).toEqual(true);
499 expect(rects.length).toEqual(test.expectedNum);
501 for (const rect of rects) {
502 expect(rect.direction.value).toEqual(CanvasKit.TextDirection.LTR.value);
503 const p = new CanvasKit.Paint();
504 p.setColor(test.color);
505 p.setStyle(CanvasKit.PaintStyle.Stroke);
506 canvas.drawRect(rect, p);
510 expect(CanvasKit.RectHeightStyle.Strut).toBeTruthy();
517 gm('paragraph_emoji', (canvas) => {
518 const fontMgr = CanvasKit.FontMgr.FromData([notoSerifFontBuffer, emojiFontBuffer]);
519 expect(fontMgr.countFamilies()).toEqual(2);
520 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
521 expect(fontMgr.getFamilyName(1)).toEqual('Noto Color Emoji');
525 const paraStyle = new CanvasKit.ParagraphStyle({
527 color: CanvasKit.BLACK,
528 // Put text first, otherwise the "emoji space" is used and that looks bad.
529 fontFamilies: ['Noto Serif', 'Noto Color Emoji'],
532 textAlign: CanvasKit.TextAlign.Left,
536 const textStyle = new CanvasKit.TextStyle({
537 color: CanvasKit.BLACK,
538 // The number 4 matches an emoji and looks strange w/o this additional style.
539 fontFamilies: ['Noto Serif'],
543 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
544 builder.pushStyle(textStyle);
545 builder.addText('4 flags on following line:\n');
547 builder.addText(`🏳️🌈 🇮🇹 🇱🇷 🇺🇸\n`);
548 builder.addText('Rainbow Italy Liberia USA\n\n');
549 builder.addText('Emoji below should wrap:\n');
550 builder.addText(`🍕🍔🍟🥝🍱🕶🎩👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧👩👩👦👩👩👧👧`);
551 const paragraph = builder.build();
553 paragraph.layout(wrapTo);
555 canvas.clear(CanvasKit.WHITE);
556 canvas.drawParagraph(paragraph, 10, 10);
558 const paint = new CanvasKit.Paint();
559 paint.setColor(CanvasKit.RED);
560 paint.setStyle(CanvasKit.PaintStyle.Stroke);
561 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
569 gm('paragraph_hits', (canvas) => {
570 const fontMgr = CanvasKit.FontMgr.FromData([notoSerifFontBuffer]);
574 const paraStyle = new CanvasKit.ParagraphStyle({
576 color: CanvasKit.BLACK,
577 fontFamilies: ['Noto Serif'],
580 textAlign: CanvasKit.TextAlign.Left,
583 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
584 builder.addText('UNCOPYRIGHTABLE');
585 const paragraph = builder.build();
587 paragraph.layout(wrapTo);
589 canvas.clear(CanvasKit.WHITE);
590 canvas.translate(10, 10);
591 canvas.drawParagraph(paragraph, 0, 0);
593 const paint = new CanvasKit.Paint();
595 paint.setColor(CanvasKit.Color(255, 0, 0));
596 paint.setStyle(CanvasKit.PaintStyle.Fill);
597 canvas.drawCircle(20, 30, 3, paint);
599 paint.setColor(CanvasKit.Color(0, 0, 255));
600 canvas.drawCircle(80, 90, 3, paint);
602 paint.setColor(CanvasKit.Color(0, 255, 0));
603 canvas.drawCircle(280, 2, 3, paint);
605 let posU = paragraph.getGlyphPositionAtCoordinate(20, 30);
606 expect(posU).toEqual({
608 affinity: CanvasKit.Affinity.Upstream
610 let posA = paragraph.getGlyphPositionAtCoordinate(80, 90);
611 expect(posA).toEqual({
613 affinity: CanvasKit.Affinity.Downstream
615 let posG = paragraph.getGlyphPositionAtCoordinate(280, 2);
616 expect(posG).toEqual({
618 affinity: CanvasKit.Affinity.Upstream
627 gm('paragraph_styles', (canvas) => {
628 const paint = new CanvasKit.Paint();
630 paint.setColor(CanvasKit.RED);
631 paint.setStyle(CanvasKit.PaintStyle.Stroke);
633 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
637 const paraStyle = new CanvasKit.ParagraphStyle({
639 fontFamilies: ['Noto Serif'],
642 weight: CanvasKit.FontWeight.Light,
645 textDirection: CanvasKit.TextDirection.RTL,
646 disableHinting: true,
649 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
650 builder.addText('Default text\n');
652 const boldItalic = new CanvasKit.TextStyle({
653 color: CanvasKit.RED,
654 fontFamilies: ['Noto Serif'],
657 weight: CanvasKit.FontWeight.Bold,
658 width: CanvasKit.FontWidth.Expanded,
659 slant: CanvasKit.FontSlant.Italic,
662 builder.pushStyle(boldItalic);
663 builder.addText(`Bold, Expanded, Italic\n`);
665 builder.addText(`back to normal`);
666 const paragraph = builder.build();
668 paragraph.layout(wrapTo);
670 canvas.clear(CanvasKit.WHITE);
672 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
673 canvas.drawParagraph(paragraph, 10, 10);
681 gm('paragraph_font_provider', (canvas) => {
682 const paint = new CanvasKit.Paint();
684 paint.setColor(CanvasKit.RED);
685 paint.setStyle(CanvasKit.PaintStyle.Stroke);
687 // Register Noto Serif as 'sans-serif'.
688 const fontSrc = CanvasKit.TypefaceFontProvider.Make();
689 fontSrc.registerFont(notoSerifFontBuffer, 'sans-serif');
690 fontSrc.registerFont(notoSerifBoldItalicFontBuffer, 'sans-serif');
694 const paraStyle = new CanvasKit.ParagraphStyle({
696 fontFamilies: ['sans-serif'],
699 weight: CanvasKit.FontWeight.Light,
702 textDirection: CanvasKit.TextDirection.RTL,
703 disableHinting: true,
706 const builder = CanvasKit.ParagraphBuilder.MakeFromFontProvider(paraStyle, fontSrc);
707 builder.addText('Default text\n');
709 const boldItalic = new CanvasKit.TextStyle({
710 color: CanvasKit.RED,
711 fontFamilies: ['sans-serif'],
714 weight: CanvasKit.FontWeight.Bold,
715 width: CanvasKit.FontWidth.Expanded,
716 slant: CanvasKit.FontSlant.Italic,
719 builder.pushStyle(boldItalic);
720 builder.addText(`Bold, Expanded, Italic\n`);
722 builder.addText(`back to normal`);
723 const paragraph = builder.build();
725 paragraph.layout(wrapTo);
727 canvas.clear(CanvasKit.WHITE);
729 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
730 canvas.drawParagraph(paragraph, 10, 10);
738 gm('paragraph_text_styles', (canvas) => {
739 const paint = new CanvasKit.Paint();
741 paint.setColor(CanvasKit.GREEN);
742 paint.setStyle(CanvasKit.PaintStyle.Stroke);
744 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
745 expect(fontMgr.countFamilies()).toEqual(1);
746 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
750 const paraStyle = new CanvasKit.ParagraphStyle({
752 color: CanvasKit.BLACK,
753 fontFamilies: ['Noto Serif'],
755 decoration: CanvasKit.UnderlineDecoration,
756 decorationThickness: 1.5, // multiplier based on font size
757 decorationStyle: CanvasKit.DecorationStyle.Wavy,
761 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
762 builder.addText('VAVAVAVAVAVAVA\nVAVA\n');
764 const blueText = new CanvasKit.TextStyle({
765 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
766 color: CanvasKit.Color(48, 37, 199),
767 fontFamilies: ['Noto Serif'],
768 textBaseline: CanvasKit.TextBaseline.Ideographic,
769 decoration: CanvasKit.LineThroughDecoration,
770 decorationThickness: 1.5, // multiplier based on font size
772 builder.pushStyle(blueText);
773 builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`);
775 builder.addText(` I'm done with the blue now. `);
776 builder.addText(`Now I hope we should stop before we get 8 lines tall. `);
777 const paragraph = builder.build();
779 paragraph.layout(wrapTo);
781 expect(paragraph.getAlphabeticBaseline()).toBeCloseTo(21.377, 3);
782 expect(paragraph.getHeight()).toEqual(227);
783 expect(paragraph.getIdeographicBaseline()).toBeCloseTo(27.236, 3);
784 expect(paragraph.getLongestLine()).toBeCloseTo(195.664, 3);
785 expect(paragraph.getMaxIntrinsicWidth()).toBeCloseTo(1167.140, 3);
786 expect(paragraph.getMaxWidth()).toEqual(200);
787 expect(paragraph.getMinIntrinsicWidth()).toBeCloseTo(172.360, 3);
788 // Check "VAVAVAVAVAVAVA"
789 expect(paragraph.getWordBoundary(8)).toEqual({
794 expect(paragraph.getWordBoundary(25)).toEqual({
799 canvas.clear(CanvasKit.WHITE);
800 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
801 canvas.drawParagraph(paragraph, 10, 10);
809 gm('paragraph_text_styles_mixed_leading_distribution', (canvas) => {
810 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
811 expect(fontMgr.countFamilies()).toEqual(1);
812 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
816 const paraStyle = new CanvasKit.ParagraphStyle({
818 color: CanvasKit.BLACK,
819 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
820 fontFamilies: ['Noto Serif'],
822 heightMultiplier: 10,
826 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
827 builder.addText('Not half leading');
829 const halfLeadingText = new CanvasKit.TextStyle({
830 color: CanvasKit.Color(48, 37, 199),
831 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
832 fontFamilies: ['Noto Serif'],
834 heightMultiplier: 10,
837 builder.pushStyle(halfLeadingText);
838 builder.addText('Half Leading Text');
839 const paragraph = builder.build();
841 paragraph.layout(wrapTo);
842 canvas.clear(CanvasKit.WHITE);
843 canvas.drawParagraph(paragraph, 0, 0);
850 gm('paragraph_mixed_text_height_behavior', (canvas) => {
851 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
852 expect(fontMgr.countFamilies()).toEqual(1);
853 expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
854 canvas.clear(CanvasKit.WHITE);
855 const paint = new CanvasKit.Paint();
856 paint.setColor(CanvasKit.RED);
857 paint.setStyle(CanvasKit.PaintStyle.Stroke);
860 const behaviors = ["All", "DisableFirstAscent", "DisableLastDescent", "DisableAll"];
862 for (let i = 0; i < behaviors.length; i++) {
863 const style = new CanvasKit.ParagraphStyle({
865 color: CanvasKit.BLACK,
866 fontFamilies: ['Noto Serif'],
868 heightMultiplier: 3, // make the difference more obvious
870 textHeightBehavior: CanvasKit.TextHeightBehavior[behaviors[i]],
872 const builder = CanvasKit.ParagraphBuilder.Make(style, fontMgr);
873 builder.addText('Text height behavior\nof '+behaviors[i]);
874 const paragraph = builder.build();
875 paragraph.layout(wrapTo);
876 canvas.drawParagraph(paragraph, 0, 150 * i);
877 canvas.drawRect(CanvasKit.LTRBRect(0, 150 * i, wrapTo, 150 * i + 120), paint);
885 it('should not crash if we omit font family on pushed textStyle', () => {
886 const surface = CanvasKit.MakeCanvasSurface('test');
887 expect(surface).toBeTruthy('Could not make surface');
889 const canvas = surface.getCanvas();
890 const paint = new CanvasKit.Paint();
892 paint.setColor(CanvasKit.RED);
893 paint.setStyle(CanvasKit.PaintStyle.Stroke);
895 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
899 const paraStyle = new CanvasKit.ParagraphStyle({
901 fontFamilies: ['Noto Serif'],
904 textDirection: CanvasKit.TextDirection.RTL,
905 disableHinting: true,
908 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
909 builder.addText('Default text\n');
911 const boldItalic = new CanvasKit.TextStyle({
913 weight: CanvasKit.FontWeight.Bold,
914 slant: CanvasKit.FontSlant.Italic,
917 builder.pushStyle(boldItalic);
918 builder.addText(`Bold, Italic\n`); // doesn't show up, but we don't crash
920 builder.addText(`back to normal`);
921 const paragraph = builder.build();
923 paragraph.layout(wrapTo);
925 canvas.clear(CanvasKit.WHITE);
926 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
927 canvas.drawParagraph(paragraph, 10, 10);
937 it('should not crash if we omit font family on paragraph style', () => {
938 const surface = CanvasKit.MakeCanvasSurface('test');
939 expect(surface).toBeTruthy('Could not make surface');
941 const canvas = surface.getCanvas();
942 const paint = new CanvasKit.Paint();
944 paint.setColor(CanvasKit.RED);
945 paint.setStyle(CanvasKit.PaintStyle.Stroke);
947 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
951 const paraStyle = new CanvasKit.ParagraphStyle({
955 textDirection: CanvasKit.TextDirection.RTL,
956 disableHinting: true,
959 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
960 builder.addText('Default text\n');
962 const boldItalic = new CanvasKit.TextStyle({
964 weight: CanvasKit.FontWeight.Bold,
965 slant: CanvasKit.FontSlant.Italic,
968 builder.pushStyle(boldItalic);
969 builder.addText(`Bold, Italic\n`);
971 builder.addText(`back to normal`);
972 const paragraph = builder.build();
974 paragraph.layout(wrapTo);
976 canvas.clear(CanvasKit.WHITE);
977 canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
978 canvas.drawParagraph(paragraph, 10, 10);
988 gm('paragraph builder with reset', (canvas) => {
989 canvas.clear(CanvasKit.WHITE);
990 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
994 const paraStyle = new CanvasKit.ParagraphStyle({
1000 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1001 builder.addText('Default text\n');
1003 const boldItalic = new CanvasKit.TextStyle({
1005 weight: CanvasKit.FontWeight.Bold,
1006 slant: CanvasKit.FontSlant.Italic,
1009 builder.pushStyle(boldItalic);
1010 builder.addText(`Bold, Italic\n`);
1012 const paragraph = builder.build();
1013 paragraph.layout(wrapTo);
1016 builder.addText('This builder has been reused\n');
1018 builder.pushStyle(boldItalic);
1019 builder.addText(`2 Bold, Italic\n`);
1021 builder.addText(`2 back to normal`);
1022 const paragraph2 = builder.build();
1023 paragraph2.layout(wrapTo);
1025 canvas.drawParagraph(paragraph, 10, 10);
1026 canvas.drawParagraph(paragraph2, 10, 100);
1029 paragraph2.delete();
1034 // This helped find and resolve skbug.com/13247
1035 gm('paragraph saved to skpicture', (canvas) => {
1036 canvas.clear(CanvasKit.WHITE);
1037 const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
1041 const paraStyle = new CanvasKit.ParagraphStyle({
1047 const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1048 builder.addText('This was saved to an SkPicture\n');
1050 const boldItalic = new CanvasKit.TextStyle({
1052 weight: CanvasKit.FontWeight.Bold,
1053 slant: CanvasKit.FontSlant.Italic,
1056 builder.pushStyle(boldItalic);
1057 builder.addText(`Bold, Italic\n`);
1059 const paragraph = builder.build();
1060 paragraph.layout(wrapTo);
1062 const recorder = new CanvasKit.PictureRecorder();
1063 const skpCanvas = recorder.beginRecording(CanvasKit.LTRBRect(0, 0, 200, 200));
1064 skpCanvas.drawParagraph(paragraph, 10, 10);
1065 const picture = recorder.finishRecordingAsPicture();
1067 canvas.drawPicture(CanvasKit.MakePicture(picture.serialize()));