Update rive-cpp to 2.0 version
[platform/core/uifw/rive-tizen.git] / submodule / skia / modules / canvaskit / tests / bazel / paragraph_test.js
1 describe('Paragraph Behavior', function() {
2     let container;
3
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(
8         (buffer) => {
9             notoSerifFontBuffer = buffer;
10         });
11
12     let notoSerifBoldItalicFontBuffer = null;
13     const notoSerifBoldItalicFontLoaded = fetch('/assets/NotoSerif-BoldItalic.ttf').then(
14         (response) => response.arrayBuffer()).then(
15         (buffer) => {
16             notoSerifBoldItalicFontBuffer = buffer;
17         });
18
19     let emojiFontBuffer = null;
20     const emojiFontLoaded = fetch('/assets/NotoColorEmoji.ttf').then(
21         (response) => response.arrayBuffer()).then(
22         (buffer) => {
23             emojiFontBuffer = buffer;
24         });
25
26     let robotoFontBuffer = null;
27     const robotoFontLoaded = fetch('/assets/Roboto-Regular.otf').then(
28         (response) => response.arrayBuffer()).then(
29         (buffer) => {
30             robotoFontBuffer = buffer;
31         });
32
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);
44     });
45
46     afterEach(() => {
47         document.body.removeChild(container);
48     });
49
50     gm('paragraph_basic', (canvas) => {
51         const paint = new CanvasKit.Paint();
52
53         paint.setColor(CanvasKit.RED);
54         paint.setStyle(CanvasKit.PaintStyle.Stroke);
55
56         const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
57         expect(fontMgr.countFamilies()).toEqual(1);
58         expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
59
60         const wrapTo = 200;
61
62         const paraStyle = new CanvasKit.ParagraphStyle({
63             textStyle: {
64                 color: CanvasKit.BLACK,
65                 fontFamilies: ['Noto Serif'],
66                 fontSize: 20,
67             },
68             textAlign: CanvasKit.TextAlign.Center,
69             maxLines: 8,
70             ellipsis: '.._.',
71         });
72
73         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
74         builder.addText('VAVAVAVAVAVAVA\nVAVA\n');
75
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
82             fontSize: 24,
83         });
84         builder.pushStyle(blueText);
85         builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`)
86         builder.pop();
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();
90
91         paragraph.layout(wrapTo);
92
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({
102             start: 0,
103             end: 14,
104         });
105         expect(paragraph.getWordBoundary(25)).toEqual({
106             start: 25,
107             end: 26,
108         });
109
110
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);
126
127         canvas.clear(CanvasKit.WHITE);
128         canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
129         canvas.drawParagraph(paragraph, 10, 10);
130
131         paint.delete();
132         fontMgr.delete();
133         paragraph.delete();
134         builder.delete();
135     });
136
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');
141
142         const wrapTo = 200;
143
144         const paraStyle = new CanvasKit.ParagraphStyle({
145             textStyle: {
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'],
150                 fontSize: 20,
151             },
152
153             textAlign: CanvasKit.TextAlign.Center,
154         });
155         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
156         builder.addText(
157             'This text has a red foregroundColor and a blue backgroundColor.');
158         const paragraph = builder.build();
159         paragraph.layout(300);
160
161         canvas.clear(CanvasKit.WHITE);
162         canvas.drawParagraph(paragraph, 10, 10);
163
164         fontMgr.delete();
165         paragraph.delete();
166         builder.delete();
167     });
168
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');
173
174         const wrapTo = 200;
175
176         const textStyle = {
177             fontFamilies: ['Noto Serif'],
178             fontSize: 40,
179         };
180         const paraStyle = new CanvasKit.ParagraphStyle({
181             textStyle: textStyle,
182             textAlign: CanvasKit.TextAlign.Center,
183         });
184         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
185
186         const fg = new CanvasKit.Paint();
187         fg.setColor(CanvasKit.BLACK);
188         fg.setStyle(CanvasKit.PaintStyle.Stroke);
189
190         const bg = new CanvasKit.Paint();
191         bg.setColor(CanvasKit.TRANSPARENT);
192
193         builder.pushPaintStyle(textStyle, fg, bg);
194         builder.addText(
195             'This text is stroked in black and has no fill');
196         const paragraph = builder.build();
197         paragraph.layout(300);
198
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);
203
204         fg.delete();
205         bg.delete();
206         fontMgr.delete();
207         paragraph.delete();
208         builder.delete();
209     });
210
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');
215
216         const wrapTo = 200;
217
218         const paraStyle = new CanvasKit.ParagraphStyle({
219             textStyle: {
220                 // color should default to black
221                 fontFamilies: ['Noto Serif'],
222                 fontSize: 20,
223                 letterSpacing: 5,
224                 wordSpacing: 10,
225             },
226
227             textAlign: CanvasKit.TextAlign.Center,
228         });
229         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
230         builder.addText(
231             'This text should have a lot of space between the letters and words.');
232         const paragraph = builder.build();
233         paragraph.layout(300);
234
235         canvas.clear(CanvasKit.WHITE);
236         canvas.drawParagraph(paragraph, 10, 10);
237
238         fontMgr.delete();
239         paragraph.delete();
240         builder.delete();
241     });
242
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');
247
248         const wrapTo = 200;
249
250         const paraStyle = new CanvasKit.ParagraphStyle({
251             textStyle: {
252                 color: CanvasKit.WHITE,
253                 fontFamilies: ['Noto Serif'],
254                 fontSize: 20,
255                 shadows: [{color: CanvasKit.BLACK, blurRadius: 15},
256                           {color: CanvasKit.RED, blurRadius: 5, offset: [10, 10]}],
257             },
258
259             textAlign: CanvasKit.TextAlign.Center,
260         });
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);
265
266         canvas.clear(CanvasKit.WHITE);
267         canvas.drawParagraph(paragraph, 10, 10);
268
269         fontMgr.delete();
270         paragraph.delete();
271         builder.delete();
272     });
273
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');
278
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({
282             textStyle: {
283                 fontFamilies: ['Roboto'],
284                 color: CanvasKit.BLACK,
285             },
286             strutStyle: {
287                 strutEnabled: true,
288                 fontFamilies: ['Roboto'],
289                 fontSize: 28,
290                 heightMultiplier: 1.5,
291                 forceStrutHeight: true,
292             },
293         });
294         const paraStyle = new CanvasKit.ParagraphStyle({
295             textStyle: {
296                 fontFamilies: ['Roboto'],
297                 color: CanvasKit.BLACK,
298             },
299         });
300         const roboto28Style = new CanvasKit.TextStyle({
301             color: CanvasKit.BLACK,
302             fontFamilies: ['Roboto'],
303             fontSize: 28,
304         });
305         const roboto32Style = new CanvasKit.TextStyle({
306             color: CanvasKit.BLACK,
307             fontFamilies: ['Roboto'],
308             fontSize: 32,
309         });
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');
315         builder.pop();
316         builder.pushStyle(roboto28Style);
317         builder.addText('a strut style!\n');
318         builder.pop();
319         builder.pop();
320
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');
326         builder2.pop();
327         builder2.pushStyle(roboto28Style);
328         builder2.addText('a strut style!\n');
329         builder2.pop();
330         builder2.pop();
331
332         const paragraph = builder.build();
333         paragraph.layout(300);
334
335         const paragraph2 = builder2.build();
336         paragraph2.layout(300);
337
338         canvas.clear(CanvasKit.WHITE);
339         canvas.drawParagraph(paragraph, 10, 10);
340         canvas.drawParagraph(paragraph2, 220, 10);
341
342         fontMgr.delete();
343         paragraph.delete();
344         builder.delete();
345     });
346
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');
351
352
353         const paraStyle = new CanvasKit.ParagraphStyle({
354             textStyle: {
355                 color: CanvasKit.BLACK,
356                 fontFamilies: ['Roboto'],
357                 fontSize: 30,
358                 fontFeatures: [{name: 'smcp', value: 1}]
359             },
360             textAlign: CanvasKit.TextAlign.Center,
361         });
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);
366
367         canvas.clear(CanvasKit.WHITE);
368         canvas.drawParagraph(paragraph, 10, 10);
369
370         fontMgr.delete();
371         paragraph.delete();
372         builder.delete();
373     });
374
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');
379
380
381         const paraStyle = new CanvasKit.ParagraphStyle({
382             textStyle: {
383                 color: CanvasKit.BLACK,
384                 fontFamilies: ['Roboto'],
385                 fontSize: 20,
386             },
387             textAlign: CanvasKit.TextAlign.Center,
388         });
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');
394
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');
399
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);
405
406         let rects = paragraph.getRectsForPlaceholders();
407
408         canvas.clear(CanvasKit.WHITE);
409         canvas.drawParagraph(paragraph, 10, 10);
410
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.
416             const placeholder =
417                 CanvasKit.LTRBRect(rect[0]+10,rect[1]+10,rect[2]+10,rect[3]+10);
418             canvas.drawRect(placeholder, p);
419             p.delete();
420         }
421
422         fontMgr.delete();
423         paragraph.delete();
424         builder.delete();
425     });
426
427     // loosely based on SkParagraph_GetRectsForRangeParagraph test in c++ code.
428     gm('paragraph_rects', (canvas) => {
429         const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
430
431         const wrapTo = 550;
432         const hStyle = CanvasKit.RectHeightStyle.Max;
433         const wStyle = CanvasKit.RectWidthStyle.Tight;
434
435         const mallocedColor = CanvasKit.Malloc(Float32Array, 4);
436         mallocedColor.toTypedArray().set([0.9, 0.1, 0.1, 1.0]);
437
438         const paraStyle = new CanvasKit.ParagraphStyle({
439             textStyle: {
440                 color: mallocedColor,
441                 fontFamilies: ['Noto Serif'],
442                 fontSize: 50,
443             },
444             textAlign: CanvasKit.TextAlign.Left,
445             maxLines: 10,
446         });
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);
451
452         paragraph.layout(wrapTo);
453
454         const ranges = [
455             {
456                 start: 0,
457                 end: 0,
458                 expectedNum: 0,
459             },
460             {
461                 start: 0,
462                 end: 1,
463                 expectedNum: 1,
464                 color: CanvasKit.Color(200, 0, 200),
465             },
466             {
467                 start: 2,
468                 end: 8,
469                 expectedNum: 1,
470                 color: CanvasKit.Color(255, 0, 0),
471             },
472             {
473                 start: 8,
474                 end: 21,
475                 expectedNum: 1,
476                 color: CanvasKit.Color(0, 255, 0),
477             },
478             {
479                 start: 30,
480                 end: 100,
481                 expectedNum: 4,
482                 color: CanvasKit.Color(0, 0, 255),
483             },
484             {
485                 start: 19,
486                 end: 22,
487                 expectedNum: 1,
488                 color: CanvasKit.Color(0, 200, 200),
489             }
490         ];
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);
495
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);
500
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);
507                 p.delete();
508             }
509         }
510         expect(CanvasKit.RectHeightStyle.Strut).toBeTruthy();
511
512         fontMgr.delete();
513         paragraph.delete();
514         builder.delete();
515     });
516
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');
522
523         const wrapTo = 450;
524
525         const paraStyle = new CanvasKit.ParagraphStyle({
526             textStyle: {
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'],
530                 fontSize: 30,
531             },
532             textAlign: CanvasKit.TextAlign.Left,
533             maxLines: 10,
534         });
535
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'],
540             fontSize: 30,
541         });
542
543         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
544         builder.pushStyle(textStyle);
545         builder.addText('4 flags on following line:\n');
546         builder.pop();
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();
552
553         paragraph.layout(wrapTo);
554
555         canvas.clear(CanvasKit.WHITE);
556         canvas.drawParagraph(paragraph, 10, 10);
557
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);
562
563         fontMgr.delete();
564         paint.delete();
565         builder.delete();
566         paragraph.delete();
567     });
568
569     gm('paragraph_hits', (canvas) => {
570         const fontMgr = CanvasKit.FontMgr.FromData([notoSerifFontBuffer]);
571
572         const wrapTo = 300;
573
574         const paraStyle = new CanvasKit.ParagraphStyle({
575             textStyle: {
576                 color: CanvasKit.BLACK,
577                 fontFamilies: ['Noto Serif'],
578                 fontSize: 50,
579             },
580             textAlign: CanvasKit.TextAlign.Left,
581             maxLines: 10,
582         });
583         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
584         builder.addText('UNCOPYRIGHTABLE');
585         const paragraph = builder.build();
586
587         paragraph.layout(wrapTo);
588
589         canvas.clear(CanvasKit.WHITE);
590         canvas.translate(10, 10);
591         canvas.drawParagraph(paragraph, 0, 0);
592
593         const paint = new CanvasKit.Paint();
594
595         paint.setColor(CanvasKit.Color(255, 0, 0));
596         paint.setStyle(CanvasKit.PaintStyle.Fill);
597         canvas.drawCircle(20, 30, 3, paint);
598
599         paint.setColor(CanvasKit.Color(0, 0, 255));
600         canvas.drawCircle(80, 90, 3, paint);
601
602         paint.setColor(CanvasKit.Color(0, 255, 0));
603         canvas.drawCircle(280, 2, 3, paint);
604
605         let posU = paragraph.getGlyphPositionAtCoordinate(20, 30);
606         expect(posU).toEqual({
607             pos: 1,
608             affinity: CanvasKit.Affinity.Upstream
609         });
610         let posA = paragraph.getGlyphPositionAtCoordinate(80, 90);
611         expect(posA).toEqual({
612             pos: 11,
613             affinity: CanvasKit.Affinity.Downstream
614         });
615         let posG = paragraph.getGlyphPositionAtCoordinate(280, 2);
616         expect(posG).toEqual({
617             pos: 9,
618             affinity: CanvasKit.Affinity.Upstream
619         });
620
621         builder.delete();
622         paragraph.delete();
623         paint.delete();
624         fontMgr.delete();
625     });
626
627     gm('paragraph_styles', (canvas) => {
628         const paint = new CanvasKit.Paint();
629
630         paint.setColor(CanvasKit.RED);
631         paint.setStyle(CanvasKit.PaintStyle.Stroke);
632
633         const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
634
635         const wrapTo = 250;
636
637         const paraStyle = new CanvasKit.ParagraphStyle({
638             textStyle: {
639                 fontFamilies: ['Noto Serif'],
640                 fontSize: 20,
641                 fontStyle: {
642                     weight: CanvasKit.FontWeight.Light,
643                 }
644             },
645             textDirection: CanvasKit.TextDirection.RTL,
646             disableHinting: true,
647         });
648
649         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
650         builder.addText('Default text\n');
651
652         const boldItalic = new CanvasKit.TextStyle({
653             color: CanvasKit.RED,
654             fontFamilies: ['Noto Serif'],
655             fontSize: 20,
656             fontStyle: {
657                 weight: CanvasKit.FontWeight.Bold,
658                 width: CanvasKit.FontWidth.Expanded,
659                 slant: CanvasKit.FontSlant.Italic,
660             }
661         });
662         builder.pushStyle(boldItalic);
663         builder.addText(`Bold, Expanded, Italic\n`);
664         builder.pop();
665         builder.addText(`back to normal`);
666         const paragraph = builder.build();
667
668         paragraph.layout(wrapTo);
669
670         canvas.clear(CanvasKit.WHITE);
671
672         canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
673         canvas.drawParagraph(paragraph, 10, 10);
674
675         paint.delete();
676         paragraph.delete();
677         builder.delete();
678         fontMgr.delete();
679     });
680
681     gm('paragraph_font_provider', (canvas) => {
682         const paint = new CanvasKit.Paint();
683
684         paint.setColor(CanvasKit.RED);
685         paint.setStyle(CanvasKit.PaintStyle.Stroke);
686
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');
691
692         const wrapTo = 250;
693
694         const paraStyle = new CanvasKit.ParagraphStyle({
695             textStyle: {
696                 fontFamilies: ['sans-serif'],
697                 fontSize: 20,
698                 fontStyle: {
699                     weight: CanvasKit.FontWeight.Light,
700                 }
701             },
702             textDirection: CanvasKit.TextDirection.RTL,
703             disableHinting: true,
704         });
705
706         const builder = CanvasKit.ParagraphBuilder.MakeFromFontProvider(paraStyle, fontSrc);
707         builder.addText('Default text\n');
708
709         const boldItalic = new CanvasKit.TextStyle({
710             color: CanvasKit.RED,
711             fontFamilies: ['sans-serif'],
712             fontSize: 20,
713             fontStyle: {
714                 weight: CanvasKit.FontWeight.Bold,
715                 width: CanvasKit.FontWidth.Expanded,
716                 slant: CanvasKit.FontSlant.Italic,
717             }
718         });
719         builder.pushStyle(boldItalic);
720         builder.addText(`Bold, Expanded, Italic\n`);
721         builder.pop();
722         builder.addText(`back to normal`);
723         const paragraph = builder.build();
724
725         paragraph.layout(wrapTo);
726
727         canvas.clear(CanvasKit.WHITE);
728
729         canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
730         canvas.drawParagraph(paragraph, 10, 10);
731
732         paint.delete();
733         paragraph.delete();
734         builder.delete();
735         fontSrc.delete();
736     });
737
738     gm('paragraph_text_styles', (canvas) => {
739         const paint = new CanvasKit.Paint();
740
741         paint.setColor(CanvasKit.GREEN);
742         paint.setStyle(CanvasKit.PaintStyle.Stroke);
743
744         const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer);
745         expect(fontMgr.countFamilies()).toEqual(1);
746         expect(fontMgr.getFamilyName(0)).toEqual('Noto Serif');
747
748         const wrapTo = 200;
749
750         const paraStyle = new CanvasKit.ParagraphStyle({
751             textStyle: {
752                 color: CanvasKit.BLACK,
753                 fontFamilies: ['Noto Serif'],
754                 fontSize: 20,
755                 decoration: CanvasKit.UnderlineDecoration,
756                 decorationThickness: 1.5, // multiplier based on font size
757                 decorationStyle: CanvasKit.DecorationStyle.Wavy,
758             },
759         });
760
761         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
762         builder.addText('VAVAVAVAVAVAVA\nVAVA\n');
763
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
771         });
772         builder.pushStyle(blueText);
773         builder.addText(`Gosh I hope this wraps at some point, it is such a long line.`);
774         builder.pop();
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();
778
779         paragraph.layout(wrapTo);
780
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({
790             start: 0,
791             end: 14,
792         });
793         // Check "I"
794         expect(paragraph.getWordBoundary(25)).toEqual({
795             start: 25,
796             end: 26,
797         });
798
799         canvas.clear(CanvasKit.WHITE);
800         canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, 230), paint);
801         canvas.drawParagraph(paragraph, 10, 10);
802
803         paint.delete();
804         fontMgr.delete();
805         paragraph.delete();
806         builder.delete();
807     });
808
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');
813
814         const wrapTo = 200;
815
816         const paraStyle = new CanvasKit.ParagraphStyle({
817             textStyle: {
818                 color: CanvasKit.BLACK,
819                 backgroundColor: CanvasKit.Color(234, 208, 232), // light pink
820                 fontFamilies: ['Noto Serif'],
821                 fontSize: 10,
822                 heightMultiplier: 10,
823             },
824         });
825
826         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
827         builder.addText('Not half leading');
828
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'],
833             fontSize: 10,
834             heightMultiplier: 10,
835             halfLeading: true,
836         });
837         builder.pushStyle(halfLeadingText);
838         builder.addText('Half Leading Text');
839         const paragraph = builder.build();
840
841         paragraph.layout(wrapTo);
842         canvas.clear(CanvasKit.WHITE);
843         canvas.drawParagraph(paragraph, 0, 0);
844
845         fontMgr.delete();
846         paragraph.delete();
847         builder.delete();
848     });
849
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);
858
859         const wrapTo = 220;
860         const behaviors = ["All", "DisableFirstAscent", "DisableLastDescent", "DisableAll"];
861
862         for (let i = 0; i < behaviors.length; i++) {
863             const style = new CanvasKit.ParagraphStyle({
864                 textStyle: {
865                     color: CanvasKit.BLACK,
866                     fontFamilies: ['Noto Serif'],
867                     fontSize: 20,
868                     heightMultiplier: 3, // make the difference more obvious
869                 },
870                 textHeightBehavior: CanvasKit.TextHeightBehavior[behaviors[i]],
871             });
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);
878             paragraph.delete();
879             builder.delete();
880         }
881         paint.delete();
882         fontMgr.delete();
883     });
884
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');
888
889         const canvas = surface.getCanvas();
890         const paint = new CanvasKit.Paint();
891
892         paint.setColor(CanvasKit.RED);
893         paint.setStyle(CanvasKit.PaintStyle.Stroke);
894
895         const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
896
897         const wrapTo = 250;
898
899         const paraStyle = new CanvasKit.ParagraphStyle({
900             textStyle: {
901                 fontFamilies: ['Noto Serif'],
902                 fontSize: 20,
903             },
904             textDirection: CanvasKit.TextDirection.RTL,
905             disableHinting: true,
906         });
907
908         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
909         builder.addText('Default text\n');
910
911         const boldItalic = new CanvasKit.TextStyle({
912             fontStyle: {
913                 weight: CanvasKit.FontWeight.Bold,
914                 slant: CanvasKit.FontSlant.Italic,
915             }
916         });
917         builder.pushStyle(boldItalic);
918         builder.addText(`Bold, Italic\n`); // doesn't show up, but we don't crash
919         builder.pop();
920         builder.addText(`back to normal`);
921         const paragraph = builder.build();
922
923         paragraph.layout(wrapTo);
924
925         canvas.clear(CanvasKit.WHITE);
926         canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
927         canvas.drawParagraph(paragraph, 10, 10);
928
929         surface.flush();
930
931         paragraph.delete();
932         builder.delete();
933         paint.delete();
934         fontMgr.delete();
935     });
936
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');
940
941         const canvas = surface.getCanvas();
942         const paint = new CanvasKit.Paint();
943
944         paint.setColor(CanvasKit.RED);
945         paint.setStyle(CanvasKit.PaintStyle.Stroke);
946
947         const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
948
949         const wrapTo = 250;
950
951         const paraStyle = new CanvasKit.ParagraphStyle({
952             textStyle: {
953                 fontSize: 20,
954             },
955             textDirection: CanvasKit.TextDirection.RTL,
956             disableHinting: true,
957         });
958
959         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
960         builder.addText('Default text\n');
961
962         const boldItalic = new CanvasKit.TextStyle({
963             fontStyle: {
964                 weight: CanvasKit.FontWeight.Bold,
965                 slant: CanvasKit.FontSlant.Italic,
966             }
967         });
968         builder.pushStyle(boldItalic);
969         builder.addText(`Bold, Italic\n`);
970         builder.pop();
971         builder.addText(`back to normal`);
972         const paragraph = builder.build();
973
974         paragraph.layout(wrapTo);
975
976         canvas.clear(CanvasKit.WHITE);
977         canvas.drawRect(CanvasKit.LTRBRect(10, 10, wrapTo+10, wrapTo+10), paint);
978         canvas.drawParagraph(paragraph, 10, 10);
979
980         surface.flush();
981
982         paragraph.delete();
983         paint.delete();
984         fontMgr.delete();
985         builder.delete();
986     });
987
988     gm('paragraph builder with reset', (canvas) => {
989         canvas.clear(CanvasKit.WHITE);
990         const fontMgr = CanvasKit.FontMgr.FromData(notoSerifFontBuffer, notoSerifBoldItalicFontBuffer);
991
992         const wrapTo = 250;
993
994         const paraStyle = new CanvasKit.ParagraphStyle({
995             textStyle: {
996                 fontSize: 20,
997             },
998         });
999
1000         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1001         builder.addText('Default text\n');
1002
1003         const boldItalic = new CanvasKit.TextStyle({
1004             fontStyle: {
1005                 weight: CanvasKit.FontWeight.Bold,
1006                 slant: CanvasKit.FontSlant.Italic,
1007             }
1008         });
1009         builder.pushStyle(boldItalic);
1010         builder.addText(`Bold, Italic\n`);
1011         builder.pop();
1012         const paragraph = builder.build();
1013         paragraph.layout(wrapTo);
1014
1015         builder.reset();
1016         builder.addText('This builder has been reused\n');
1017
1018         builder.pushStyle(boldItalic);
1019         builder.addText(`2 Bold, Italic\n`);
1020         builder.pop();
1021         builder.addText(`2 back to normal`);
1022         const paragraph2 = builder.build();
1023         paragraph2.layout(wrapTo);
1024
1025         canvas.drawParagraph(paragraph, 10, 10);
1026         canvas.drawParagraph(paragraph2, 10, 100);
1027
1028         paragraph.delete();
1029         paragraph2.delete();
1030         fontMgr.delete();
1031         builder.delete();
1032     });
1033
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);
1038
1039         const wrapTo = 250;
1040
1041         const paraStyle = new CanvasKit.ParagraphStyle({
1042             textStyle: {
1043                 fontSize: 20,
1044             },
1045         });
1046
1047         const builder = CanvasKit.ParagraphBuilder.Make(paraStyle, fontMgr);
1048         builder.addText('This was saved to an SkPicture\n');
1049
1050         const boldItalic = new CanvasKit.TextStyle({
1051             fontStyle: {
1052                 weight: CanvasKit.FontWeight.Bold,
1053                 slant: CanvasKit.FontSlant.Italic,
1054             }
1055         });
1056         builder.pushStyle(boldItalic);
1057         builder.addText(`Bold, Italic\n`);
1058         builder.pop();
1059         const paragraph = builder.build();
1060         paragraph.layout(wrapTo);
1061
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();
1066
1067         canvas.drawPicture(CanvasKit.MakePicture(picture.serialize()));
1068
1069         picture.delete();
1070         recorder.delete();
1071         paragraph.delete();
1072         fontMgr.delete();
1073         builder.delete();
1074     });
1075 });